I'm trying to understand how srcset width values work, but the basic example I have is throwing me off.
Given the code below, I expect the large image to display when the viewport is > 600px. However, it actually changes at 784px. I'm not sure why this is.
<!-- image changes to large variant at 784px instead of the expected 1024px -->
<img
src="small.jpg"
srcset="small.jpg 600w,
large.jpg 1024w"
alt="A test image">
Currently Chrome uses a geometric mean to choose between two image candidates. So it still uses the 600 image because 1024 is too big and therefore switches.
In an upcoming release Chrome will always switch to the higher image, if you have a low density device. On other devices Chrome will still use a a median.
Related
Tested using Google Chrome in Incognito mode and reloading the page with "empty cache and hard reload" each time.
I have the following html responsive image:
<img class="content-img" src="/app/uploads/2018/07/1400x750.png"
srcset="/app/uploads/2018/07/1400x750.png 1400w,
/app/uploads/2018/07/1400x750-768x411.png 768w,
/app/uploads/2018/07/1400x750-1280x686.png 1280w,
/app/uploads/2018/07/1400x750-520x279.png 520w,
/app/uploads/2018/07/1400x750-420x225.png 420w,
/app/uploads/2018/07/1400x750-340x182.png 340w,
/app/uploads/2018/07/1400x750-600x321.png 600w"
sizes="(max-width: 666px) 100vw, (max-width: 1399px) 38vw, 535px"
>
Expected Behaviour:
1. Viewport Widths 0px - 666px:
Browser should take the full viewport pixel width e.g. 450px, and select the smallest src from the srcset where width is greater than 450px, in this case '/app/uploads/2018/07/1400x750-520x279.png'
2. Viewport Widths 667px - 1399px:
Browser should take 38% of the viewport width e.g. 380px # 1000px viewport, and select the smallest src from the srcset where width is greater than 380px, in this case '/app/uploads/2018/07/1400x750-420x225.png'
3. Viewport Widths 1400+ px:
Browser should take the default of 535px and select the smallest src from the srcset where width is greater than 535px, in this case '/app/uploads/2018/07/1400x750-600x321.png'
Actual Behaviour:
Testing in Google Chrome, using dev tools inspect element on the img for all of the above examples, the resulting "CurrentSrc" in each case is:
/app/uploads/2018/07/1400x750-520x279.png (CORRECT)
/app/uploads/2018/07/1400x750-1280x686.png (INCORRECT (expecting 420px width)
/app/uploads/2018/07/1400x750.png (INCORRECT (expecting 600px width)
I'm left scratching my head, other similar questions all seem to boil this down to a Google Chrome caching issue, but I've been careful to eliminate that issue when testing and I still don't get the src images I expect.
I'm only 90% sure I've written the correct "sizes" attribute for the behaviour I want. Note that the logic is slightly complex due to lining up with responsive CSS breakpoints and trying to load sensible image widths in context.
Some clarification on how the srcset and sizes attributes define the way the browser should choose which image to display (see Responsive images for more details).
First, the browser checks the sizes attribute to find the first media condition that applies to the current device width. So, for the breakpoints you specified, the browser should display the selected image at full viewport width for devices up to 666px wide, then at 38% of viewport width for devices between 667px and 1399px wide, and lastly at a fixed 535px width for devices greater than 1399px wide.
Second, the browser checks the srcset attribute to find the image that most closely matches the image slot size as determined by the sizes attribute (as described above).
So for the breakpoints you specified, expect the following:
1) For devices up to 666px wide, the browser should select the image width that is closest to the device width (not the smallest image that is greater than the device width).
Examples:
On a 450px wide device, the browser should select the 420w image.
On a 599px wide device, the browser should select the 600w image.
2) For devices between 667px and 1399px wide, the browser should select the image width that is closest to 38% of the device width.
Examples:
On a 1000px wide device, the browser should select either the 340w image or the 420w image (not sure how it chooses when you split the difference since the image slot size determined by the media query is 380px)
On a 1366px wide device, the browser should select the 520w image (since the slot size determined by the media query is 519px)
3) For devices over 1399px wide, the browser should select the 520w image (since the slot size determine by the media query is a fixed 535px).
NOTE: Retina and other high-res displays change the math a bit, resulting in the browser more or less doubling the width of image that it chooses in each of the above examples (see Responsive images: if you're just changing resolutions, use srcset).
A couple potential gotchas to double check. Make sure you have <meta name="viewport" content="width=device-width"> in the head so the devices you test on are forced to adopt their real width when loading the page. Also make sure you don't have conflicting css or js interfering with the display of your image.
If you have avoided the gotchas, your code otherwise looks fine and the similar snippet below gives the expected results for me, though you do have to be careful about caching when testing as you already noticed (slightly different organization might help you more quickly scan which images should be chosen under different circumstances). Below is a snippet using placeholder images that show their widths which may help your testing.
<img srcset="https://via.placeholder.com/340x182 340w,
https://via.placeholder.com/420x225 420w,
https://via.placeholder.com/520x279 520w,
https://via.placeholder.com/600x321 600w,
https://via.placeholder.com/768x411 768w,
https://via.placeholder.com/1280x686 1280w,
https://via.placeholder.com/1400x750 1400w"
sizes="(max-width: 666px) 100vw,
(max-width: 1399px) 38vw,
535px"
src="https://via.placeholder.com/340x182"
alt="placeholder image">
I have noticed that order matters when it comes to srcset.
For some reason from the biggest image to the smallest one worked, whilst the contrary did not.
I am not entirely sure whether it's an actual thing or not, but I am leaving this out here with the hope it may help someone.
Example that did work:
<img srcset="{{block.settings.slide-image | img_url:'1250x' }} 1227w
{{block.settings.slide-image | img_url:'1100x' }} 1079w,
{{block.settings.slide-image | img_url:'956x' }} 936w,
{{block.settings.slide-image | img_url:'795x' }} 775w,
{{block.settings.slide-image | img_url:'620x' }} 600w,
{{block.settings.slide-image | img_url:'520x' }} 500w,
{{block.settings.slide-image | img_url:'456x' }} 436w"
src="{{block.settings.slide-image | img_url:'456x' }}" />
Example that did not work:
<img srcset="{{block.settings.slide-image | img_url:'456x' }} 436w,
{{block.settings.slide-image | img_url:'520x' }} 500w,
{{block.settings.slide-image | img_url:'620x' }} 600w,
{{block.settings.slide-image | img_url:'795x' }} 775w,
{{block.settings.slide-image | img_url:'956x' }} 936w,
{{block.settings.slide-image | img_url:'1100x' }} 1079w,
{{block.settings.slide-image | img_url:'1250x' }} 1227w"
src="{{block.settings.slide-image | img_url:'456x' }}" />
The Missing Ingredient To Understanding What The Browser Is Thinking: devicePixelRatio
The reason this is a source of so much confusion, is that at first glance, we seem to think that all the calculations should be done on the basis of pixels.
Let's walk through the thought process / algorithm of the browser:
How big is the screen? It's a mobile phone: 390px.
How much space does the image occupy? 100vw (100% of the viewport)
If we only factored in this much, we would say: "Okay, use the 400px or 500px image. That should cover it."
But, there is another aspect. Nowadays, phones don't have such low resolutions. They are packed with pixels! This packing is expressed in: devicePixelRatio (can be checked with JS with window.devicePixelRatio)
If you factor that in, you have to ask another question. What is the total number of pixels to be covered:
Phone Width In Pixels x devicePixelRatio
So, that is many more pixels. And that is why the browser chooses the larger image.
Great article which clearly explains all this with images and samples: srcset not working? 😩 Getting Wrong Images? Let’s Find Out Why!
What's Going On In The Above Case?
Well, we cannot tell because we don't know what device this code is running on. Or what the device pixel ratio is. Once that is known, I am sure that things will immediately make sense.
I have read about this problem quite often so far and it also occurs for my own projects. Here is an introduction of what I have found out so far about the srcset and the sizes attribte.
There are two different possibilities on how to use the srcset-attribute (source w3c: http://w3c.github.io/html/semantics-embedded-content.html#device-pixel-ratio):
Device-pixel-ratio-based selection when the rendered size of the image is fixed
This is a simple and reliable way to use srcset. You simply say: If the device-pixel ratio of the target devicer is bigger than x, display this image with the following higher resolution.
The x descriptor is not appropriate when the rendered size of the
image depends on the viewport width (viewport-based selection), but
can be used together with art direction.
Example:
<img src="/uploads/100-marie-lloyd.jpg"
srcset="/uploads/150-marie-lloyd.jpg 1.5x, /uploads/200-marie-lloyd.jpg 2x"
alt="" width="100" height="150">
Viewport-based selection
This method allows you to display different image sizes depending on the size of your viewport. This is the method you are primarily using within your example.
The srcset and sizes attributes can be used, using the w descriptor,
to provide multiple images that only vary in their size (the smaller
image is a scaled-down version of the bigger image).
Simple example:
<h1><img sizes="100vw" srcset="wolf-400.jpg 400w, wolf-800.jpg 800w, wolf-1600.jpg 1600w"
src="wolf-400.jpg" alt="The rad wolf"></h1>
One step further: Using the sizes attribute
The default for Viewport-based selection and srcset is, that the image always has 100% width (100vw). The sizes attribute is giving the great possibility to tell the browser, how the width of an image is at a certain screen width.
The sizes attribute sets up the layout breakpoints at 30em and 50em,
and declares the image sizes between these breakpoints to be 100vw,
50vw, or calc(33vw - 100px). These sizes do not necessarily have to
match up exactly with the actual image width as specified in the CSS.
The user agent will pick a width from the sizes attribute, using the
first item with a (the part in parentheses) that
evaluates to true, or using the last item (calc(33vw - 100px)) if they
all evaluate to false.
Example:
<img sizes="(max-width: 30em) 100vw, (max-width: 50em) 50vw, calc(33vw - 100px)"
srcset="swing-200.jpg 200w, swing-400.jpg 400w, swing-800.jpg 800w, swing-1600.jpg 1600w"
src="swing-400.jpg" alt="Kettlebell Swing">
Here is the challange where I would be really glad if someone could enlighten me
Can I rely on srcset that a Client always load the correct image? Or is the actually loaded image also depending on processing power and internet connection speed as some people stated? I had complaints about retina devices which load lower resolution images.
How can I use both: Device-pixel-ratio-based and viewport-based selection together? Because for each possible size in sizes, I may want to define a retina image with 200% size as well as a non-retina image.
And furthermore: Does it make sense to use different images within srcset for different viewport sizes or is this a misuse of the srcset attribute? If it is possible to combine device-pixel-ratio-based and viewport-based selection, this should also be possible.
Can I rely on srcset that a Client always load the correct image?
The answer is NO. Moreso, you can never know the dimension of the image the user will upload unless you want to check that with a Javascript code and then restrict the user to upload the right dimension. But that will not be too user friendly.
Again, You might want to implement an algorithm to always resize the image to the particular size you want without distorting the quality, so you don't have to pass different imageurl to the srcset and just use the src attr. This can be an advantage to users with slow internet connection.
Does it make sense to use different images within srcset for different viewport sizes or is this a misuse of the srcset attribute?
The point is how many device Viewport do you want to handle in all. If you specify different image sizes and dimension for different view port, you might not able to target all at once especially when a new device is available that you didn't handle as at the time you were developing.
I am looking for a responsive image strategy that allows to serve different jpg quality based on the device pixel density.
On a small screen with high resolution, I would serve a low-quality but high-resolution jpg. On a big screen with low pixel density, I would serve a high-quality jpg, ideally matching the device resolution.
QUESTION:
Is this somehow possible with <img srcset=".." sizes=".." />?
Background / scenario
Different original images with different original dimensions.
Different image display contexts: As a gallery thumbnail, embedded in a blog post, in a modal box, full screen..
Responsive layout, with media queries that change the display size of those images, not necessarily proportional.
E.g. a what is displayed as a 100px thumbnail on desktop, might be displayed in full width on mobile.
High-resolution or "Retina" devices, with a resolution multiplier. On these I want many pixels, but low file size.
Solutions I'm considering
I think the promising approach for this is <img srcset=".." sizes=".."/>.
However, I am wondering if or how I should combine the x-descriptor and the w-descriptor.
The x-descriptor specifies a relative size. But relative to what? Both the original image size and the layout width of the <img> can vary between contexts and between viewports. The viewport reports a width for the media queries, but the actual pixel width can be 2x or 3x the reported viewport width, thanks to retina displays.
The w-descriptor specifies an absolute size. This sounds way better for image contexts that could be in thumbnail size on desktop, and full width on mobile - or vice versa.
Questions / Related
How could I serve different jpg quality depending on the pixel density on the device? (question as above)
Related question: Do srcset and sizes refer to device pixels or layout pixels?
You can do something like this
<picture>
<source media="(min-resolution: 1.5dppx)"
srcset="low-quality-high-res.jpg 2x">
<img src="high-quality-low-res.jpg" ...>
</picture>
In practice you probably want to have multiple sizes for each quality:
<picture>
<source media="(min-resolution: 1.5dppx)"
srcset="lq-500w.jpg 500w, lq-1000w.jpg 1000w"
sizes="100vw">
<img src="hq-250w.jpg"
srcset="hq-250w.jpg 250w, hq-500w.jpg 500w"
sizes="100vw" ...>
</picture>
(And change sizes as appropriate depending on context.)
Picture element is getting widely and quickly spread (http://caniuse.com/#search=picture), and I think it is a great way to avoid serving oversized/undersized pictures, specially when you want to display the same picture on mobile and desktop at 100% of the viewport width.
That can be solved like this:
<img
srcset="large.jpg 1920w,
medium.jpg 720w,
small.jpg 360w"
src="medium.jpg">
This allows the browser to be clever and decide which picture to load, but I find a problem with this approach: many mobile devices have a pixel density of 2 or more! Therefore when displaying it for 360w we would actually need the medium image if we want that image to look sharp. It could be done like this:
<picture>
<source srcset="http://goo.gl/LsuU9t" media="(min-width: 720px)">
<source srcset="http://goo.gl/LsuU9t" media="(min-width: 360px and min-resolution: 2dppx)">
<img src="http://goo.gl/LsuU9t">
</picture>
The problem here, in my opinion, is that this can grow as much as screen resolution grows and we lose the benefits of browser cleverly deciding the best option.
So, my question is if there is a halfway point between the two, so I can still separate between html and css.
The browser takes the pixel density into account when selecting an image. So a device with 360 CSS px wide viewport and a 2x pixel density would select medium.jpg. This is exactly what the w descriptor and the sizes attribute are designed to solve! Don't use picture here.
Also see https://ericportis.com/posts/2014/srcset-sizes/
I want to know how could I start using the HTML srcset img attribute in my mobile apps. Or Is there any other jQuery plugin which helps me to solve image resolution problem.
<img srcset="banner-HD.jpeg 2x, banner-phone.jpeg 100w, banner-phone-HD.jpeg 100w 2x" alt="Banner Image" />
In short, Srcset is a new attribute which allows you to specify different kind of images for different screen-sizes/orientation/display-types. The usage is really simple, you just provide a lot of different images separating them with a comma like this: <img src="image.jpg" alt="image" srcset="<img> <descriptor>, ..., <img_n> <descriptor_n>">. Here is an example: srcset="image.jpg 160w, image2.jpg 320w, image3.jpg 2x"
This is a longer answer which explains things in more details.
Difference between srcset and picture. Both srcset and picture does approximately the same things, but there is a subtle difference: picture dictates what image the browser should use, whereas srcset gives the browser a choice. A lot of things can be used to select this choice like viewport size, users preferences, network condition and so on. The support for srcset is pretty good and almost all current browsers more or less support it. Situation with a picture element is a little bit worse.
Descriptors are just a way to show what kind of image is hidden behind the resource. There are various kinds of descriptors:
density descriptor. srcset="image.jpg, image-2X.jpg 2x"
The display density values—the 1x, 2x, etc.—are referred to as display density descriptors. If a display density descriptor isn’t provided, it is assumed to be 1x. Good variant to target retina displays.
width descriptor. srcset="image-240.jpg 240w, image-640.jpg 640w". I am sure this is self explanatory. The only problem is that by itself width descriptor is not really helpful. Why? read here
size descriptor, which only makes sense if you use width descriptor. srcset="image-160.jpg 160w, image-320.jpg 320w, image-640.jpg 640w, image-1280.jpg 1280w" sizes="(max-width: 480px) 100vw, (max-width: 900px) 33vw, 254px">. The instructions for the browser would look like this: (max-width: 480px) 100vw — if the viewport is 480 pixels wide or smaller, the image will be 100% of the viewport width. (max-width: 900px) 33vw — if the viewport is 480 pixels wide or smaller, this rule will never be reached because of the previous media condition. And 254px is when there is no media condition listed, the length is assumed to be a default value used when none of the other media conditions are met.
Just for completeness will add here that there is an image-set() attribute for a background image in CSS and some other helpful link here
Here is a detailed guide on srcset along with code samples.
srcset allows you to define a list of different image resources along with size information so that browser can pick the most appropriate image based on the actual device’s resolution.
Each comma-separated item in srcset has:
Image URL, e.g. http://ik.imagekit.io/demo/default-image.jpg or relative path /demo/default-image.jpg
An empty space
The actual width of the image or display density:
Either using display density descriptor, for example, 1.5x, 2x etc.
Or, using width descriptors, for example, 450w. This is the width of the image in pixels.
Using display density descriptor
The syntax for display density descriptors is straightforward. srcset provides a comma-separated list of image resources along with display density it should be used, for example1x, 2x etc.
<img src="image.jpg"
srcset="image.jpg,
image_2x.jpg 2x"
/>
Live demo - https://imagekitio.github.io/responsive-images-guide/srcset-density.html.
Using width descriptor
The syntax is similar to the display density descriptor, but instead of display density values, we provide the actual width of the image.
<img src="image.jpg"
srcset="small.jpg 300w,
medium.jpg 600w,
large.jpg 900w"
/>
This lets the browser pick the best image
Using width descriptor allows the browser to pick the best candidate from srcset based on the actual width needed to render that image on that particular display at runtime.
Note that display pixel density is also taken into account by the browser while calculating the required width. 😎
For example, assuming an image takes up the whole viewport width - On a 300px wide screen with DPR 2, the browser will pick medium.jpg because it needs a 300x2=600px wide image. On a 300px wide screen with DPR value 3, the browser will select large.jpg because it needs a 300x3=900px wide image.
Demo - srcset with width descriptor
Let see this in action with a live demo - https://imagekitio.github.io/responsive-images-guide/srcset-width.html.
Here is a good article on the srcset attribute and how to use it. srcet allows you to declare a set of images to be displayed on different viewport sizes. You just have to save and image at different resolutions e.g. banner-phone-HD.jpeg would be the highest resolution.
Exmaple:
<img alt="my awesome image"
src="banner.jpeg"
srcset="banner-HD.jpeg 2x, banner-phone.jpeg 640w, banner-phone-HD.jpeg 640w 2x">
The above would serve banner-phone.jpeg to devices with viewport width under 640px, banner-phone-HD.jpeg to small screen high DPI devices, banner-HD.jpeg to high DPI devices with screens greater than 640px, and banner.jpeg to everything else.
There are also other methods like CSS media queries you can use to produce the same effect.
I am not aware of any JQuery plugins which would help with this.