Skip to content
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

[nuxt3] Support responsive image sizes/srcset for pixel-device-ratio > 1 (HD and Retina screens, iPad, ..) #618

Open
hartmut-co-uk opened this issue Sep 16, 2022 · 14 comments
Assignees
Labels
enhancement New feature or request

Comments

@hartmut-co-uk
Copy link
Collaborator

hartmut-co-uk commented Sep 16, 2022

Feature Request

Support (nuxt3) nuxt/image with vercel provider for static app -> nuxt generate.

Example Repo

https://github.com/hartmut-co-uk/nuxt3-image-pixel-device-ratio-2x-support

Context

On ‘normal’ screens, using the viewport width seems sufficient to choose the correct size of the image. But with HD and Retina screens you want to serve bigger sizes of the image. Et voilá, here comes srcset and sizes! With srcset and sizes the browser also takes into account the pixel density of the screen.

To calculate the pixel density of a screen, browsers make use of the pixel-device-ratio. A pixel-device-ratio of 1 means that one device pixel corresponds to one CSS pixel. A pixel-device-ratio of 2 means that two device pixels corresponds to one CSS pixel.

For example, an iPad Air 2 has a screen resolution of 2048 by 1536 pixels (source: Wikipedia). The pixel-device-ratio of this iPad is 2. Which means that for the browser, the screen is 1024 by 768 pixels.

Source: https://medium.com/@woutervanderzee/responsive-images-with-srcset-and-sizes-fc434845e948

Demo

Hosted on vercel: https://nuxt3-image-pixel-device-ratio-2x-support.vercel.app/

PageSpeed Insights

Lighthouse Scan: https://pagespeed.web.dev/report?url=https%3A%2F%2Fnuxt3-image-pixel-device-ratio-2x-support.vercel.app%2F&form_factor=desktop

Conclusion

I'd like nuxt/image to somehow allow srcset to be created which includes pixel-device-ratio 2x (maybe others, too?) such as the manually coded option (4) which includes srcset options for [250w, 500w, 1000w].

This is the only solution which provides both

  1. efficiency -> appropriately-sized to save mobile data and improve load time
  2. quality -> bigger (sharp) images on HD/Retina/iPad with high screen pixel density

on all devices.

Rendered as

<img
    src="/cat.png"
    alt="(4) Custom responsive plain &lt;img&gt; with pixel-device-ratio=2 support (retina, ipad, ...)"
    class="w-full"
    width="1200"
    height="630"
    srcset="/_vercel/image?url=/cat.png&w=250&q=100 250w, /_vercel/image?url=/cat.png&w=500&q=100 500w, /_vercel/image?url=/cat.png&w=1000&q=100 1000w"
    sizes="(min-width: 640px) 500px, (min-width: 1024px) 1000px, 100vw"/>

⚠️ Note: I'm actually not quite sure if the sizes is correct or if it's going to be ignored by the browser anyway - cause the browser chooses from srcset for the best choice considering pixel-device-ratio?!?
...happy to be put right by someone with more insight!

@hartmut-co-uk
Copy link
Collaborator Author

hartmut-co-uk commented Sep 17, 2022

As for a solution on how to extend <nuxt-img> - how about adding 2x (3x?, ..?) props?

<nuxt-img
            src="/cat.png"
            alt="(5) &lt;nuxt-img&gt; with sizes defined for 1x size & and prop `2x` to also have double pixel density srcset generated"
            class="w-full"
            width="1200"
            height="630"
            2x
            sizes="sm:250px lg:500px"/>

Alternatives ideas

  • prop density2x (, density3 , ...?)
  • prop pixel-device-ratio="1 2 3"
  • prop densities="1x 2x 3x"

@hartmut-co-uk
Copy link
Collaborator Author

Would you consider support for this to be added, any preference on which props to go for?

How about adding following boolean props? Or would that make it too restricted?
Though I think this should solve most use cases?

  '2x': { type: Boolean, default: undefined },
  '3x': { type: Boolean, default: undefined },
  '4x': { type: Boolean, default: undefined },

like for preload

preload: { type: Boolean, default: undefined },

Can I go ahead and create a PR?
https://github.com/nuxt/image/pull/459/files by @SimonBackx seemed mostly complete, but was abandoned?

@hartmut-co-uk
Copy link
Collaborator Author

Astro uses widths in addition to sizes to explicitly define image variants to be provided.
I think this would also be a good + straight option?!?
ref: https://docs.astro.build/en/guides/integrations-guide/image/#widths

@SnooHD
Copy link

SnooHD commented Oct 17, 2022

This is a very common issue, and as suggested in the comments above a PR was already created by @SimonBackx
Why is this not a priority to fix?

Thanks for diving into this @hartmut-co-uk, I really hope this will give the extra push it needs.

@slavanossar
Copy link

slavanossar commented Nov 25, 2022

Astro uses widths in addition to sizes to explicitly define image variants to be provided. I think this would also be a good + straight option?!? ref: docs.astro.build/en/guides/integrations-guide/image/#widths

This is a good solution, the current sizes prop:

  1. repurposes an existing HTML attribute and makes it function differently (confusing).
  2. forces you to specify the image transform size and the expected display width in a single definition (???).

Even better would be giving the option to globally define transform widths in the module config, and the srcset of every <NuxtImg> would contain the same transforms (see nuxt-responsive-loader).

Generating specific transform URLs based on the expected widths of every unique image element across an app makes no sense, and defeats the point of srcset:

  1. For every image generate an array of known transforms across different widths
  2. Define expected display widths for each <img> using the sizes attribute
  3. Let the browser automatically pick the closest match from srcset, while also taking into account devicePixelRatio

@hartmut-co-uk
Copy link
Collaborator Author

hartmut-co-uk commented Nov 25, 2022

On the second part I don't agree. Requirements can be very different, the render/display size.
E.g. an avatar/profile picture vs. images you might render in full width of your main section and/or are able to enlarge / view in fullscreen.

Of course, sizes attribute serves as a hint to modern browsers only.
But when e.g. preloading via link (<head>), the responsive instructions via sizes still hold value to the browser's decision which variant to preload, based on window size and DPR.

@slavanossar
Copy link

Yeah that is fair, would be good to have the option for both, quick and easy global transforms or more granular, separate controls for transforms and display sizes.

@joaobarcia
Copy link

This would be extremely valuable to us as well. Subscribing to notifications. Thanks for all the work!

@w550
Copy link

w550 commented Feb 8, 2023

。。。

@w550
Copy link

w550 commented Feb 8, 2023

img.png
img@2x.png
img_h5.png
img_h5@2x.png
md:h5 lg:pc

@shadow81627
Copy link
Contributor

shadow81627 commented Apr 3, 2023

I've got a similar issue with DPI 2x, where I've got a full width image that is always 500px height. On mobile width 320px DPI 2x a 640x500 image is being selected by the browser from the srcset. This is an issue since the image aspect ratio is wrong. The DPI 1x image is 320x500 and the DPI 2x image needs to be 640x1000 rather than 640x500.

Example NuxtPicture usage for fixed height 100% width

<NuxtPicture src="/cat.png" :height="500" fit="cover"></NuxtPicture>

Output

<picture>
    <source type="image/webp" sizes="(max-width: 320px) 320px, (max-width: 640px) 640px, (max-width: 768px) 768px, (max-width: 1024px) 1024px, (max-width: 1280px) 1280px, (max-width: 1536px) 1536px, 1536px" srcset="/_ipx/f_webp&amp;fit_cover&amp;s_320x500/cat.png 320w, /_ipx/f_webp&amp;fit_cover&amp;s_640x500/cat.png 640w, /_ipx/f_webp&amp;fit_cover&amp;s_768x500/cat.png 768w, /_ipx/f_webp&amp;fit_cover&amp;s_1024x500/cat.png 1024w, /_ipx/f_webp&amp;fit_cover&amp;s_1280x500/cat.png 1280w, /_ipx/f_webp&amp;fit_cover&amp;s_1536x500/cat.png 1536w, /_ipx/f_webp&amp;fit_cover&amp;s_1536x500/cat.png 1536w">
    <img height="500" data-nuxt-pic="" src="/_ipx/f_jpeg&amp;fit_cover&amp;s_1536x500/cat.png" sizes="(max-width: 320px) 320px, (max-width: 640px) 640px, (max-width: 768px) 768px, (max-width: 1024px) 1024px, (max-width: 1280px) 1280px, (max-width: 1536px) 1536px, 1536px" srcset="/_ipx/f_jpeg&amp;fit_cover&amp;s_320x500/cat.png 320w, /_ipx/f_jpeg&amp;fit_cover&amp;s_640x500/cat.png 640w, /_ipx/f_jpeg&amp;fit_cover&amp;s_768x500/cat.png 768w, /_ipx/f_jpeg&amp;fit_cover&amp;s_1024x500/cat.png 1024w, /_ipx/f_jpeg&amp;fit_cover&amp;s_1280x500/cat.png 1280w, /_ipx/f_jpeg&amp;fit_cover&amp;s_1536x500/cat.png 1536w, /_ipx/f_jpeg&amp;fit_cover&amp;s_1536x500/cat.png 1536w">
</picture>

@danielroe danielroe added the enhancement New feature or request label Jun 8, 2023
@leopoldkristjansson
Copy link

I am a little confused, the live docs have chapters called densities:

But it looks to me like using the density settings are not yet working - and obviously, this issue is still open as well as this PR https://github.com/nuxt/image/pull/227/files

So I assume, densities have not yet been implemented?

Thanks!

@hartmut-co-uk
Copy link
Collaborator Author

Hi everyone, with #769 a first step has been implemented - just as the docs say.
I think the docs reflect the main branch until 1.0.0 GA is reached.

So densities can be used with latest @nuxt/image-edge.

Note: Please don't consider the API stabilised until next RC/GA. Discussions are still ongoing - that's why I've kept the issue open.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

8 participants