4.0.0 ā€¢ Published 12 days ago

ember-responsive-image v4.0.0

Weekly downloads
546
License
MIT
Repository
github
Last release
12 days ago

ember-responsive-image

CI npm version Ember Observer Score

An ember-cli addon to automatically generate resized images at build-time, optimized for the responsive web, and using components to render them easily as <picture> elements.

Key Features

šŸŒ‡ Supports basic PNG and JPEG formats, as well as next-gen WebP and AVIF, for best image quality at low file sizes.

šŸŽ Super fast image processing, thanks to the awesome sharp library.

šŸ“± Layout modes for fixed sizes (with 1x and 2x image variants) as well as responsive layouts (srcset with optimized image sizes across all devices).

šŸ’Æ Lazy rendering by default, with optimized content-visibility and decoding settings and optimized markup, to prevent CLS (Cumulative Layout Shift), a core Web Vital and Lighthouse metric.

ā³ Supports advanced LQIP (Low Quality Image Placeholder) techniques to show a preview while loading, using different configurable strategies like a blurry low-res image, BlurHash or a simple dominant color.

āœØ Octane-based, written mostly in TypeScript, using Glimmer components, supporting FastBoot and Embroider, and fully tested.

ļøāš™ Flexible configuration options

Advanced optimization techniques inspired amongst others by this blog post.

Compatibility

  • Ember.js v3.16 or above
  • Ember CLI v2.13 or above
  • Node.js v10 or above

Getting started

Install in ember-cli application

In your application's directory:

ember install ember-responsive-image

Add a basic configuration

Add a basic configuration to your ember-cli-build.js, to point the addon to where your images are that:

module.exports = function (defaults) {
  let app = new EmberAddon(defaults, {
    'responsive-image': [
      {
        include: 'assets/images/**/*',
        widths: [2048, 1536, 1080, 750, 640],
      }
    ]
  });
};

Find more details about all available configuration options below.

Basic Usage

The <ResponsiveImage/> component

In a template you can use the <ResponsiveImage/> component. The image argument is required and must be a path to one of the configured image files:

<ResponsiveImage @src="assets/images/dog.jpg"/>

This will generate an <img> element wrapped in <picture> referencing all the resized images in the different formats, for the browser to decide which image it can support and fits best given the current context (device, screen size, user preferences like low bandwidth etc.):

<picture>
  <source srcset="/assets/images/dog1920w.avif 1920w, /assets/images/dog1280w.avif 1280w, /assets/images/dog640w.avif 640w, /assets/images/dog320w.avif 320w" type="image/avif">
  <source srcset="/assets/images/dog1920w.webp 1920w, /assets/images/dog1280w.webp 1280w, /assets/images/dog640w.webp 640w, /assets/images/dog320w.webp 320w" type="image/webp">
  <source srcset="/assets/images/dog1920w.jpg 1920w, /assets/images/dog1280w.jpg 1280w, /assets/images/dog640w.jpg 640w, /assets/images/dog320w.jpg 320w" type="image/jpeg">
  <img src="1920" height="1280" class="eri-responsive" loading="lazy" decoding="async">
</picture>

The image in the src attribute is calculated by the component and will be used by browsers without <picture> support - which is basically IE11.

If your image width is not 100vw, say 70vw for example, you can specify the @size (only vw is supported as a unit for now):

<ResponsiveImage @src="assets/images/dog.jpg" @size="70"/>

This will render the corresponding sizes attribute on all <source> elements. You can also set the attribute like this if your responsive image width is more complicated:

<ResponsiveImage @src="assets/images/dog.jpg" @sizes="(min-width: 800px) 800px, 100vw"/>

Fixed layout

The example above assumed you wanted a responsive image, i.e. one that automatically takes the whole available width of its parent element. This is the default mode, and will automatically the following CSS to you image:

img {
  width: 100%;
  height: auto;
}

But this addon also supports a fixed layout with fixed image dimensions. Just provide either @width or @height to opt into that mode:

<ResponsiveImage @src="assets/images/dog.jpg" @width={{320}}/>

It will still render a <img> wrapped in a <picture>, but this time it will provide the image with the optimal width (smallest width which is equal or above the target width), and additionally a 2x variant for devices with high pixel densities:

<picture>
  <source srcset="/assets/images/dog320w.avif 1x, /assets/images/dog640w.avif 2x" type="image/avif">
  <source srcset="/assets/images/dog320w.webp 1x, /assets/images/dog640w.webp 2x" type="image/webp">
  <source srcset="/assets/images/dog320w.jpg 1x, /assets/images/dog640w.jpg 2x" type="image/jpeg">
  <img src="/assets/images/dog320w.jpg" width="320" height="213" class="eri-fixed" loading="lazy" decoding="async">
</picture>

Note it is sufficient to supply either @width or @height, the component will still render the missing attribute according to the image's aspect ratio!

Image formats

Besides the basic PNG and JPEG also the next-gen formats WebP and AVIF are supported. While every modern browser supports WebP meanwhile, AVIF is a relatively new format and not well supported yet. But given the way multiple formats are supported using this addon as described above, browsers that support one of those will load them, while other will fallback to basic PNG/JPEG.

To give you an idea of the improvements possible with these formats, here are some examples. Note that the images might not show correctly if you are not using Chrome.

A more detailed analysis can be found on Jake Archibald's blog.

JPEG

Dimensions: 640px Ɨ 427px File size: 16KB.

a dog near the costline

WebP

Dimensions: 640px Ɨ 427px File size: 10KB.

a dog near the costline

AVIF

Dimensions: 640px Ɨ 427px File size: 7KB.

a dog near the costline

LQIP

Low Quality Image Placeholder is a technique to give users a preview of the image while it is loading. This addon supports different types, all with their own tradeoffs. Based on the cute dog you saw above, you can see here how these different techniques will look like.

See the Configuration section for how to configure these.

Color

This is the most basic technique, calculating the dominant color of the image, and setting it as the background color of the images while it loads. The "cost" is basically 7bytes, for the hex code of the color.

dominant color LQIP of a dog image

Inline

This creates a very small thumbnail of the original image, wraps it into a SVG and applies a blurry filter. This is then set as a base64 encoded data-URL as the background of the image while it loads. The example below consumes 348 bytes (uncompressed).

blurry LQIP of a dog image

Blurhash

BlurHash is an encoding algorithm and library, dedicated for the very purpose of generating nice looking blurry placeholders, without the overhead of a real image format, which was never optimized for that kind of tiny images. This example consumes just 40 bytes (uncompressed).

blurry LQIP of a dog image

But the tradeoff here is that it needs a runtime library for decoding, which takes about 4.7KB (1.9KB compressed). Therefore it is less suited if you have just a few images, but shines if you need placeholders for a lot!

Configuration

Configuration of the addon is done in your ember-cli-build.js. It expects an array of configuration items, with a number of different available options:

let app = new EmberAddon(defaults, {
  'responsive-image': [
    {
      include: ['path/to/images/**/*'],
      exclude: ['path/to/images/but-not-this/**/*'],
      widths: [2048, 1536, 1080, 750, 640],
      formats: ['avif', 'webp'],
      quality: 50,
      lqip: {
        type: 'inline', 
        targetPixels: 60,
      },
      removeSource: true,
      justCopy: false,
    },
    // possible more items
  ]
});

You must define at least one configuration item, with at least include defined. But you can provide more, to create separate configurations for different images.

For example if you have a gallery of logos, of which all will be displayed with a width of max. 300px or less,it makes no sense to create very large images for these, so a setting of widths: [300, 600], would make sense here (600px for the 2x version aka "retina").

Make sure you don't have multiple include definitions accidentally overlapping! You can use exclude in this case to prevent this.

Options

  • include: Glob pattern for which images should be processed based on this configuration.
  • exclude: Optional pattern which images to exclude, takes precedence over include.
  • widths: These are the widths of the resized images.
  • formats: which image formats to produce. Supported are: avif, webp, png and jpeg. By default it will use avif, webp and whatever the origin image was (png/jpeg).
  • quality: Image quality (JPEG, WebP, AVIF)
  • lqip: Let's you opt into generating LQIPs, by setting at the typeto one of the supported values. Disabled by default!
  • lqip.type: 'inline', 'color' or 'blurhash'. See the LQIP section for more details.
  • lqip.targetPixels: Desired approximate amount of pixels to use for the placeholder (does not apply for color).
  • removeSource: If true, the (large) source images will be removed from the build.
  • justCopy: If true, the images will just be copied without resizing. This can be useful for development builds to speed things up, but should be false for production.

Note: If the width of your origin image is less than the generated should be, the image will be generated unresized.

Advanced Usage

The addons provides a service and a helper for more advances usages if required. You can also build addons that hook into the image precessing pipeline. This is described in detail in the Advanced Usage documentation.

Contributing

See the Contributing guide for details.

License

This project is licensed under the MIT License.

5.0.0-beta.1

12 days ago

5.0.0-beta.0

4 months ago

4.0.0

1 year ago

3.4.0

2 years ago

3.3.1

2 years ago

3.3.0

2 years ago

3.2.0

3 years ago

3.1.1

3 years ago

3.1.0

3 years ago

3.0.1

3 years ago

3.0.0

3 years ago

2.0.0

3 years ago

2.0.0-rc.5

3 years ago

2.0.0-rc.4

3 years ago

2.0.0-rc.3

3 years ago

2.0.0-rc.2

3 years ago

2.0.0-rc.1

3 years ago

2.0.0-rc.0

3 years ago

1.0.1

3 years ago

1.0.0

3 years ago

1.0.0-rc.7

4 years ago

1.0.0-rc.6

5 years ago

1.0.0-rc.5

6 years ago

1.0.0-rc.4

6 years ago

1.0.0-rc.3

6 years ago

1.0.0-rc.2

6 years ago

1.0.0-rc.1

6 years ago

1.0.0-rc.0

7 years ago

0.7.2

7 years ago

0.7.1

7 years ago

0.7.0

7 years ago

0.6.0

7 years ago

0.5.1

7 years ago

0.5.0

7 years ago

0.4.0

7 years ago

0.3.0

7 years ago

0.2.2

7 years ago

0.2.1

7 years ago

0.2.0

7 years ago

0.1.1

7 years ago

0.1.0

8 years ago