-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add height-based selection to srcset/sizes #2973
Comments
Examples of width- and height-constrained images:
Examples of only height-constrained images:
|
Thanks for filing @zcorpan. I'm not sure I see how height-based selection in srcset/sizes would help with my example. The problem remains of how to define the media query part in sizes:
I need a media query for the part in bold. In my example I'm using JavaScript to calculate this—I really want to be able to express this in plain CSS, which is where |
I was looking more at the unsplash page, which appears to have a width-based layout breakpoint. So for that page, the media condition in The jsbin example appears to be both width and height constrained, with some padding around the image, and the aspect ratio of the image is known. Correct? Maybe |
The JSBin example is what we're moving towards, with width and height constrained images—exactly as you said.
I saw some discussion about this in ResponsiveImagesCG/picture-element#86 and couldn't quite see how it would help in my example. I did originally try to achieve my layout using
I have found it extremely difficult to express the layout you see in the JSBin example. My requirements are:
For 1 we can use the Because all elements are constrained on the X axis by default, we have to express constraints along the Y axis as constraints along the X axis. For 2 we have to define the maximum height as a For 4 we want to repeat the layout described in 2 and 3 for the If you have any suggestions, for how to improve this now or in the future, I would love to hear them. |
@zcorpan FYI, you can opt-in to the (WIP) new photo page on Unsplash with this link: https://unsplash.com/?xp=new-photos-page:experiment (temporary link only). If you then click through to a photo, you will see the new photo page with the behaviour described above. The layout is identical to the JSBin example I posted. |
Thank you! This is extremely useful info for evaluating solutions. (Note that whatever we come up with here won't be usable immediately, it will have to be implemented and shipped in multiple browsers first, so in a few years or so...)
This is what ResponsiveImagesCG/picture-element#85 is about, and I think we should fix that together with this issue.
OK, so that is what
OK, that adds a height-based breakpoint to
plus |
@zcorpan Thanks so much for the detailed response. We may be years away from having support for these changes, but it's good to understand the constraints of what we have today, and what is being done about that. As I understand it,
As the image is contained, I think it also adds a width based breakpoint to sizes? This is why in my example I have to specify both a I think this looks like it would fit my example perfectly, although it's hard to know for sure without trying it out. |
Ah right, I missed that aspect. It would then be:
I don't think further breakpoints are needed, or rather "contain" handles it already. The browser would know which dimension to use because it knows the viewport size and the image aspect ratio would be provided by |
If I understand correctly, wouldn't the first entry in
That is, the contain width is |
I probably managed to confuse myself, sorry. It's difficult to reason about this without testing. Anyway, I realize that the min-height would need to apply on both sides of the breakpoint, since shrinking the viewport width makes the image smaller on both dimensions. I need to think through how to apply the proposal to make it work as intended. Alternatively, the proposal is still too difficult to work with, and we should come up with something else to make it simpler. |
I think w3c/csswg-drafts#544 could help.
|
@OliverJAsh as for your use case/requirements – here’s the best, simplest thing I could come up with using what’s in browsers now: https://codepen.io/eeeps/pen/VMPJzK It uses @tigt’s awesome coping-with-the-lack-of- Problems:
<img srcset="https://via.placeholder.com/150x100 150w 100h,
https://via.placeholder.com/300x200 300w 200h,
https://via.placeholder.com/600x400 600w 400h,
https://via.placeholder.com/1200x800 1200w 800h"
sizes="((min-width: 342px) and (min-height: 292px)) contain calc(100vw - 12rem) calc(100vh - 12rem), 150px"
src="https://via.placeholder.com/150x100" /> Note: 342px = min-img-width (150px) + padding (12 rem); 292px = min-img-height (100px) + padding (12rem) – consider the magic-ness of these numbers an argument for †: this is the best that I could do. A closer fit to the requirements (it reserves the correct amount of space most of the time), but at some cost of complexity. |
As for use cases, I'll toss out a couple more. Here's a sideways-scrolling site which is very cool and unsual. Click on any of the images here to get thrown into a viewport-fit lightbox. Given the ~3,000 “lightbox” repos on Github, I expect this use case is much more common. |
Another use case which only dawned on me in a conversation with @yoavweiss about Hui Jing Chen’s (awesome) talk at You Gotta Love Front End – probably the biggest potential use case of all – images in vertically-sized blocks of vertically-flowing text (like this https://www.chenhuijing.com/slides/yglf-2017/#/8). |
I ran into this organically, so I thought I'd contribute another use-case and the steps I attempted to take (in case that's helpful to folks evaluating this issue). I wanted a big ol' image to lead off this blog post about an art project I've been doing: https://tylersticka.com/journal/drawing-every-day/ The Initially I wrote the <img src="..." alt="..."
srcset="
grid-05-27-960.jpg 960w,
grid-05-27-1920.jpg 1920w,
grid-05-27-2560.jpg 2560w"
sizes="100vw"> But this often resulted in the chosen asset being too small, since the asset will extend beyond the horizontal boundaries of its container. So then I tried using <img src="..." alt="..."
srcset="
grid-05-27-960.jpg 960w,
grid-05-27-1920.jpg 1920w,
grid-05-27-2560.jpg 2560w"
sizes="
(orientation: portrait) calc(16 / 9 * 50vh),
calc(16 / 9 * 75vh)"> This kinda seemed to work in Firefox (or at least it was failing gracefully) but it seemed to cause Edge to abandon the So, ever the optimist, I tried writing this: <img src="..." alt="..."
srcset="
grid-05-27-960.jpg 538h,
grid-05-27-1920.jpg 1076h,
grid-05-27-2560.jpg 1435h"
sizes="
(orientation: portrait) 50vh,
75vh"> When that failed, I finally Googled the issue, found ResponsiveImagesCG/picture-element#86 and finally arrived here. 🙂 (I ended up just using a |
@tylersticka We had a similar problem on https://unsplash.com for the "hero image" you see on the homepage, which also uses We sampled a rough height of the container element at regular width intervals (e.g. from 200px to 2000px, every 200px) and then provided a |
@OliverJAsh That's a clever solution, thanks for sharing! A similar technique could work for my example, though it makes my head hurt a little considering my image's visible area is based on (It could also just be my end-of-workday brain being slow…) |
Anything still happening on this front? I'm also trying to create a lightbox and as is, I find it hard if not impossible to use portrait images on a landscape display in a responsive way. I can make it right in either direction (horizontally or vertically), but not both. Either the image is displayed too small/big, or a bad alternative is chosen. With For now I've solved it by giving the image a |
Hey guys, also wanted to ask if there are some news on this? We are working on an image service which works together with these native features to deliver the perfect image on the fly (also includes stuff like cropping, focus point for art direction, image compression, and much more). For some edge cases we really need to tell the browser the image height in addition to the width. |
The solution I came up with was to calculate the resulting width based on the width to height ratio when creating the //...
// This should get you the max image height, minus any margin and padding
// around the parent container.
const containerHeight = imgElm.parentElement.offsetHeight;
// For determining orientation.
const { info: { width: mainImageWidth, height: mainImageHeight } } = mainImageData;
// Used later to decide wether or not to calculate width in `size` attribute value.
const orientation = mainImageWidth >= mainImageHeight ? 'landscape' : 'portrait';
// For brevity and readability later on.
const isPortrait = orientation === 'portrait';
// Simple media query for orientation if using `object-fit: cover`.
// const media = `(orientation: ${orientation})`;
// Let browser know which images to use and their widths, as usual.
const srcSet = imageSrcs
.map(({ src, info: { width } }) => `${src} ${width}w`)
.join(',\n');
const sizes = `${imageSrcs.map(
({ info: { width, height } }) =>
// Set media query for size as you need. I just went with the image width here.
`(max-width: ${width}px) ${
// Let browser know the width of image at this media query.
// For `object-fit: contain`, calculate width for portrait, falling back to just width for landscape.
isPortrait ? `${(width / height) * width * (height / contentHeight)}px` : `${width}px`
// For `object-fit: cover`, do the inverse. Will crop vertical excess on portrait,
// horizontal excess on landscape. Fine for use as background element.
// isPortrait ? '${width}px' : `${(width / height) * width * (height / contentHeight)}px`
}`
)}`;
//... Basic concept illustrated in javascript. Update/refactor for your programming language and image data model as needed. |
Of course, with the help of JS you can get done all kinds of things. The request is about a CSS solution. |
At my agency, we build a lot of websites that use fullscreen imagery (100vh/100vw object-fit: cover). Using srcset and sizes with these sorts of layouts results in blurry images on portrait mode on a phone (because the phone this way has a small width, but a large height, and so cover has to scale up the low res imagery). |
@drewbaker: Though this is only applicable for edge cases (as for full page width/height images), it may be interesting for you: |
I also came up with a JS-based solution (for React). const LazyImg: React.FC<Props> = ({srcSet,...props}) => {
const ref = useRef<HTMLImageElement>(null);
const rect = useBoundingBox(ref);
const src = useMemo(() => {
if(srcSet?.length && rect.width > 0 && rect.height > 0) {
const aspectRatio = getAspectRatio(rect)
const sources = sortBy(srcSet, [
src => Math.abs(getAspectRatio(src) - aspectRatio),
src => src.width < rect.width,
src => src.height < rect.height,
src => {
const isBigger = src.width >= rect.width && src.height >= rect.height;
return (isBigger ? 1 : -1) * src.width * src.height;
},
])
return sources[0].url
}
return undefined;
}, [srcSet,rect])
return <img ref={ref} src={src} {...props}/>
} It takes as input a list of images with a url, width & height. Then it compares each of them with the actual size of the img element (which I have styled to fit neatly in a grid). It sorts them to find the image with the closest aspect ratio, then one that is bigger then the container, and if there is one bigger than the container, the smallest such image (to save on bandwidth and memory), otherwise the biggest image. I was hoping for something like this built-in the the browser. I just feed it my list of images and it figures out the best-fit one based on the final rendered size. You could even add 'byte size' and 'quality' as parameters if the user is on a slow connection and its more important to optimize for download speed. Full code: https://gist.github.com/mnpenner/88f5d5d2c19b7f25bc0a4dba6c3d4a55 |
@mnpenner: I try to add the height-based art-direction via |
See https://bugs.chromium.org/p/chromium/issues/detail?id=421909#c19
calc()
in MQ would be nice, but better still is probably to allow specifying the image heights directly (insrcset
andsizes
). We excluded this use case originally to reduce complexity, but since this appears to be a recurring issue for web developers, it seems worthwhile to address.Earlier issue for this: ResponsiveImagesCG/picture-element#86
The text was updated successfully, but these errors were encountered: