This is how you could swap out images based on browser viewport width

I just spent over an hour fighting with this, so if anybody else needs to be able to find the browser viewport width and then swap out various sized images to reduce load times on various devices this is how I did it.

Required Frameworks / Components / Libraries

  1. App Connect Framework
  2. Bootstrap 4 Framework
  3. Browser Component
  4. dmxFormatter.js

I have 1 Image with 4 size variations

  • image-small.jpg @ 768px wide 1:1
  • image-medium.jpg @ 992px wide 4:3
  • image-large.jpg @ 1200px wide 16:9
  • image-huge.jpg @ 2000px wide 16:9

Each image is in the same folder called pictures which is in the root of my site


Here is the code that would work well if you just had a small number of images on your page.

  1. Go to your page in design view and add an image component, in the image properties panel leave Src empty, scroll down the properties list to find Dynamic Attributes > Images > Image Source > then in the value field add the code below. NOTE: adjust code to suit your image names and folder paths.
'pictures/image-'.concat((browser1.viewport.width <= 767).then('small.jpg') || (browser1.viewport.width.inRange(768, 991)).then('medium.jpg') || (browser1.viewport.width.inRange(992, 1199)).then('large.jpg') || (browser1.viewport.width >= 1200).then('huge.jpg'))

Here is the code when you have a page fairly full of images, not much more complicated but certainly easier to read the code when you need to. This is used INSTEAD of the code above.

  1. Right Click App > Data > Variable
  2. I change the variable ID to var_pic_size then paste the following code into your Variable Properties > Value
(browser1.viewport.width <= 767).then('small') || (browser1.viewport.width.inRange(768, 991)).then('medium') || (browser1.viewport.width.inRange(992, 1199)).then('large') || (browser1.viewport.width >= 1200).then('huge')
  1. Right Click Variable > Insert After > Data > Conditional Region
  • In the Conditional Properties click the Condition Dynamic Data Picker (Lightening Bolt) and select Variable > value NOTE: Ensure all your page content is in this conditional region or the Developer Tools > Network tab will report all your images as missing and then a second later all the real image names will propagate, but if the condition only fires once the value is set then that problem is solved.
  1. Go to your page in design view and add an image component as a child of the conditional region, in the image properties panel leave Src empty, scroll down the properties list to find Dynamic Attributes > Images > Image Source > then in the value field add YOUR image name and path such as pictures/image-{{var_pic_size.value}}.jpg
  2. This second option also allows you to add the same functionality into slides however we will first need to add a second App Connect Variable, which will this time hold an array of all the full image paths. Thank you @patrick for your amazing brain. So right click after the first variable in the app structure and select Data > Variable
  3. I change the ID to var_pic_array and in the value area paste the following code, this code needs to be altered to suit your folder structure and image names.
  • In this example I have 3 different images in 4 different size variations
[{url:'pictures/image1-' + var_pic_size.value + '.jpg'},{url:'pictures/image2-' + var_pic_size.value + '.jpg'},{url:'pictures/image3-' + var_pic_size.value + '.jpg'}]
  1. In Design View, make sure you are inserting as a child of the conditional region and insert Slideshow > Slideshow
  2. In the slideshow properties add your required options, and under slides click Dynamic Slides on, then set your Slides Source to var_pic_array.value
  • NOTE: Do not try add Image or Thumb or Title or Description or Link to this as you have only targeted a single dimensional array so it will not work, if you have a need to use the other options you will need to alter the Variable into a Multidimensional array.

Please keep in mind this was the easiest way I found to do this, there might very well be much better ways, if anyone already knows of one that ONLY uses the built in Wappler libraries and components please feel free to post to this thread.

I initially tried the built in srcset attributes however when looking through what images were loading in the developer tools network pane i could see that once the browser had the largest version of my image then it no longer felt the need to load the same image in smaller sizes, which I suppose would under normal circumstances be very clever indeed, however in my circumstances my various image sizes all have different aspect ratios so a mobile device would almost receive a 1:1 image while a tablet would get a 4:3 a desktop or larger gets a 16:9.

6 Likes

Thank you for sharing.

We should make like a cheatsheet of all these tricks for better Wappler use !!

Thanks again @psweb

I found a slightly better way so I have updated the original method with a second option if you are interested.

1 Like

Very useful Paul! I think you already have found the best way to do this! A cheatsheet is a good idea - maybe something also for learnwappler.com ?

I was going to add it directly to my site first, but then i figures let me rather make sure you have looked at it first in case my way is just not a very smart one. Glad you like it, thanks. Now to figure out why this does not work on the dmx-slide components, ideas?

You should post tips here as well on your site indeed - so people can find them on both places.

Normally the dmx-slide are for static slides, for dynamic you use a data source - maybe you can make the data source dynamically adjustable with a data view that is filtered/formatted at the right viewport

Thanks George I will give that a try and report back, any yes I will add some tips i come up with to my site, I have loads of them that I use all day long.

1 Like

Hey @George, i tried, i failed, but your superhero over there Patrick got me back on track in no time at all, I have edited the first post to include instructions to add the same functionality to slideshow components too.

2 Likes

Thank you @psweb for this solution.

I just have the problem that the variables are not always loaded correctly in Firefox.


Does anyone have an idea how I can solve this?

I am not really seeing the problem in your screenshot of the inspector, all of them say 300x121 which i assume is correct, can you try explain in more detail.

Sizes are not always loaded correctly.
In this case, unfortunately, 300x121 is loaded instead of 1920x774 for large devices, but not always.
If you use Firefox and refresh the page, the values ​​are not always displayed correctly.

Can you show the code for var_banner_size

<!-- Banner Size -->
<dmx-value id="var_banner_size" dmx-bind:value="(browser1.viewport.width <= 361).then('300x121')||(browser1.viewport.width.inRange(360, 567)).then('576x233')||(browser1.viewport.width.inRange(568, 767)).then('768x310')||(browser1.viewport.width.inRange(768, 991)).then('970x391')||
  (browser1.viewport.width.inRange(992, 1024)).then('1024x413')||(browser1.viewport.width.inRange(1025, 1199)).then('1200x484')||(browser1.viewport.width >= 1200).then('1920x774')"></dmx-value>


<dmx-value id="var_banner_width" dmx-bind:value="(browser1.viewport.width <= 361).then('300')||(browser1.viewport.width.inRange(360, 567)).then('576')||(browser1.viewport.width.inRange(568, 767)).then('768')||(browser1.viewport.width.inRange(768, 991)).then('970')||(browser1.viewport.width.inRange(992, 1024)).then('1024')||(browser1.viewport.width.inRange(1025, 1199)).then('1200')||(browser1.viewport.width >= 1200).then('1920') "></dmx-value>


<dmx-value id="var_banner_height" dmx-bind:value="(browser1.viewport.width <= 361).then('121')||(browser1.viewport.width.inRange(360, 567)).then('233')||(browser1.viewport.width.inRange(568, 767)).then('310')||(browser1.viewport.width.inRange(768, 991)).then('391')||(browser1.viewport.width.inRange(992, 1024)).then('413')||(browser1.viewport.width.inRange(1025, 1199)).then('484')||(browser1.viewport.width >= 1200).then('774') "></dmx-value>


<!-- Thumb Size -->
<dmx-value id="var_thumb_size" dmx-bind:value="(browser1.viewport.width <= 767).then('200x200')||(browser1.viewport.width.inRange(768, 991)).then('250x250')||(browser1.viewport.width.inRange(992, 1199)).then('250x250')||(browser1.viewport.width &gt;= 1200).then('360x360') "></dmx-value>


<dmx-value id="var_thumb_width" dmx-bind:value="(browser1.viewport.width <= 767).then('200')||(browser1.viewport.width.inRange(768, 991)).then('250')||(browser1.viewport.width.inRange(992, 1199)).then('250')||(browser1.viewport.width >= 1200).then('360') "></dmx-value>


<dmx-value id="var_thumb_height" dmx-bind:value="(browser1.viewport.width <= 767).then('200')||(browser1.viewport.width.inRange(768, 991)).then('250')||(browser1.viewport.width.inRange(992, 1199)).then('250')||(browser1.viewport.width >= 1200).then('360') "></dmx-value>

have in mind that browser1.viewport.width is not taking in account the dev console width...

To test, what if you detatch the inspector from the browser window

See here

My suggestion, just to try

Firstly your bound width and height seem to be strings an not numbers, so change to
dmx-bind:width="var_banner_width.value.toNumber()"
dmx-bind:height="var_banner_height.value.toNumber()"

Then on var banner size, dd another set of brackets around the whole thing like this
<dmx-value id="var_banner_size" dmx-bind:value="((browser1.viewport.width <= 361).then('300x121')||(browser1.viewport.width.inRange(360, 567)).then('576x233')||(browser1.viewport.width.inRange(568, 767)).then('768x310')||(browser1.viewport.width.inRange(768, 991)).then('970x391')|| (browser1.viewport.width.inRange(992, 1024)).then('1024x413')||(browser1.viewport.width.inRange(1025, 1199)).then('1200x484')||(browser1.viewport.width >= 1200).then('1920x774'))"></dmx-value>

On var_thumb_size replace the &gt; with a proper > on the 1200 version.

Then retry

Also if i actually go to your website, it seems like the order of your head elements may be a bit off, I would prefer formatter and browser higher up the head section like maybe under routing.
In fact your head section is odd, you have dmxRouting.js twice and dmxAppConnect.js

There has got to be an app connect custom formatter solution to simplify this process?

Perhaps something like:

imagename.imageSizeSelect("size")

where size is 'xs','sm','md,'lg','xl' or 'xxl' based on standard bootstrap definition

so specifying:

imagename.imageSizeSelect("md")

you return 'myimage-md.jpg'

so images could be saved with the size in the name and the formatter takes care of the selection.

Maybe ability to override size ranges via ENV variables?

Let me play with the idea

1 Like

or perhaps just use browser1.viewport.width which would make things easier and do the calculations in the function like this

imagename.imageSizeSelect(browser1.viewport.width)

if browser1.viewport.width can be parsed internally then that parameter could even be removed or made optional like this:

imagename.imageSizeSelect()

1 Like