nystudio107 / craft-imageoptimize

Automatically create & optimize responsive image transforms, using either native Craft transforms or a service like Imgix, with zero template changes.
https://nystudio107.com/plugins/imageoptimize
Other
235 stars 37 forks source link

More robust output for art-direction use cases #330

Open MattWilcox opened 2 years ago

MattWilcox commented 2 years ago

Is your feature request related to a problem? Please describe.

Best practice is to have width and height attributes on images, so that the browser can reserve layout space prior to asset download, decode, and render. That's fine as long as all <source> definitions deliver an image that's the same aspect ratio as the <img> - you can just plop the width and height on the <img> and call it done.

You need to do more when the sources are delivering differently cropped variants at different breakpoints - the width and height values on the img may be "wrong" for the source that is to be applied; resulting in the incorrect space being reserved for layout, and a resulting shift once the asset is loaded.

You'd want this sort of Picture behaviour when you're adapting an element in a design to do more than just scale objects at different breakpoints. e.g., shifting an image+text "card" so that the image is a thin strip running vertically on some views, but horizontally on others. etc.

source when used in picture supports both width and height properties to achieve mitigation of layout jank in these cases - Image Optimize doesn't seem to output this, or really offer a way to do this manually, from what I can see in the docs.

Describe the solution you would like

I want to be able to solve for art-directed transforms by being able to output markup along these lines:

<picture>
  <!-- prefer webp, list these first. Order matters, first media to match is what's rendered - opposite of CSS rules -->
  <source media="(max-width: 359px)" srcset="..." type="image/webp" width="370" height="370"><!-- square -->
  <source media="(min-width: 680px)" srcset="..." type="image/webp" width="960" height="540"><!-- very landscape -->
  <source media="(min-width: 360px)" srcset="..." type="image/webp" width="700" height="525"><!-- landscape -->

  <!-- jpg fallbacks -->
  <source media="(max-width: 359px)" srcset="..." type="image/jpeg" width="370" height="370">
  <source media="(min-width: 680px)" srcset="..." type="image/jpeg" width="960" height="540">
  <source media="(min-width: 360px)" srcset="..." type="image/jpeg" width="700" height="525">

  <img
    loading="lazy"
    decoding="async"
    src="..."
    width="960"
    height="540" 
    alt=""
  >
</picture>

I've been able to do this with Imager / ImagerX but can't see how in Image Optimize.

Further, as I'm not 100% sure what browsers are doing in order to actually achieve this, I have in the past been using generated CSS to force this behaviour, by looping through the transformed assets to create CSS rules that will apply the correct aspect-ratio before the picture element is loaded - which again, I can't see how to accomplish with Image Optimizer:

<style>
  /* We're in CSS now so order is opposite: last match is applied */
  @media screen and (min-width: 360px) { #image-20-3689108 img { aspect-ratio: 1.3333333333333; } }
  @media screen and (min-width: 680px) { #image-20-3689108 img { aspect-ratio: 1.7777777777778; } }
  @media screen and (max-width: 359px) { #image-20-3689108 img { aspect-ratio: 1; } }
</style>

<picture id="image-20-3689108">
  <!-- prefer webp, list these first. Order matters, first media to match is what's rendered - opposite of CSS rules -->
  <source media="(max-width: 359px)" srcset="..." type="image/webp" width="370" height="370"><!-- square -->
  <source media="(min-width: 680px)" srcset="..." type="image/webp" width="960" height="540"><!-- very landscape -->
  <source media="(min-width: 360px)" srcset="..." type="image/webp" width="700" height="525"><!-- landscape -->

  <!-- jpg fallbacks -->
  <source media="(max-width: 359px)" srcset="..." type="image/jpeg" width="370" height="370">
  <source media="(min-width: 680px)" srcset="..." type="image/jpeg" width="960" height="540">
  <source media="(min-width: 360px)" srcset="..." type="image/jpeg" width="700" height="525">

  <img
    loading="lazy"
    decoding="async"
    src="..." 
    width="960"
    height="540"
    alt=""
  />
</picture>
S-n-d commented 2 years ago

This would be a great enhancement! You've made a clear description of the use case @MattWilcox. This will probably also help for lighthouse layout shift score? https://web.dev/cls/#layout-shift-score

khalwat commented 1 year ago

So you can definitely this manually already with ImageOptimize, but I agree it'd be nice to have it done in a more automated way.