The optimization of images and videos in the website mainly revolves around two aspects: reducing file size on the one hand, and lazy loading of non-critical images or video resources on the other hand

The meaning of a high-resolution screen

There are two different types of pixels: CSS pixels and device pixels. A SINGLE CSS pixel can directly correspond to a single device pixel or multiple device pixels. The more device pixels there are, the more detailed the content displayed on the screen will be.

We usually say that bitmaps produce better looks on high DPI screens, but this requires a high resolution image to take advantage of a higher device pixel count. Vector images render the same at any resolution. Bitmaps encode image data by pixel, so the more pixels there are, the larger the bitmap file will be. Increasing the resolution of the bitmap and increasing the size of the bitmap will both result in increasing the number of pixels

When we double the resolution of the physical screen, the total number of pixels increases fourfold, because the number of horizontal pixels doubles and the number of vertical pixels doubles. Therefore, a “2X” screen requires four times as many pixels as a “1x” screen!

Bitmaps and vector maps

There are two types of images, bitmaps and vector images. Vector maps use lines, points, and polygons to represent images, and bitmaps use arrays of pixels to represent images.

Vector graphics are ideal for images with simple geometric shapes such as logos, text or ICONS. Vector graphics can provide a sharp display at all resolutions and zoom levels. However, when the image is more complex, such as a photograph, vector graphics are not enough. The number of SVG tags needed can be prohibitively high, and the output may still not look realistic, in which case bitmaps should be used.

Bitmaps are great for colorful, complex images, but they don’t have good properties that are independent of resolution or scaling. When you zoom in on a bitmap, you’ll see jagged and fuzzy shapes

Optimized vector graph

All modern browsers support Vector Graphics (SVG), an XML-based image format.


      
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
<svg version="1.2" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
    x="0px" y="0px" viewBox="0 0 612 792" xml:space="preserve">
<g id="XMLID_1_">
  <g>
    <circle fill="red" stroke="black" stroke-width="2" stroke-miterlimit="10" cx="50" cy="50" r="40"/>
  </g>
</g>
</svg>
Copy the code

The example above contains a lot of metadata, such as annotations and XML namespaces, which have no effect on how the browser renders the image. Therefore, removing whitespace and remarks is an effective way to optimize vector graphics, and since SVG is an XML-based format, you can also use GZIP compression to reduce its transmission size

The bitmap

Bitmaps can be divided into 1, 4, 8, 16, 24 and 32 bit images according to bit depth. The more bits of information each pixel uses, the more available colors, the more realistic color performance, and the larger the corresponding file. An RGB image is made up of three color channels, and each channel in an 8-bit/channel RGB image has 256(2 ^ 8 = 256) possible values, meaning that the image can represent 16,777,216(256 ^ 3) colors. RGB images with 8-bit/channel (BPC) are sometimes referred to as 24-bit images (8-bit x 3-channel = 24-bit data/pixel)

Knowing the image’s bit depth and pixel count, we can calculate the image file size

10000 pixels x 3 bytes = 30000 bytes, 30000 bytes / 1024 = 29 KB

Reducing bitmap file size A simple strategy is to reduce the bit depth of the image, such as changing an RGB image from a 24-bit image (one pixel is 3 bytes) to an 8-bit image (one pixel is 1 byte), which reduces the file by 2/3

Format of the bitmap

PNG, JPEG, and WebP are all different bitmap formats. PNG and JPEG are widely supported by browsers. Modern browsers support WebP, and WebP images are smaller than JPEG and PNG images. WebP is an excellent alternative to JPEG, PNG, and GIF images. Here are PNG, JPEG, WebP features

Both WebP and JPEG offer lossless and lossy compression, where no data is lost and lossy compression reduces file size but may degrade image quality. If your image needs more detail then use PNG, PNG does not apply any lossy compression algorithms, so it will produce the highest quality image, but the file size is much higher than other formats.

GIF can be used to represent animation and is widely supported by browsers, but it is not recommended because GIF animation files are very large and you can use video instead of animation if WebP is not available

The compressed image

Images can be compressed using Imagemin, which supports a variety of image formats and is easily integrated with build tools. Imagemin can be used as a CLI and NPM module, and is built around plug-ins

Imagemin CLI

Run the following command to compress the image file in the “images/” directory and save it to the same directory (overwriting the original file):

 imagemin images/* --out-dir=images
Copy the code

Imagemin node module

You can use Imagemin with WebPack, gulp, or Grunt, or you can use Imagemin alone. This code uses the Imagemin-MozJPEG plug-in to compress JPEG files to 50 qualities (” 0 “is the worst; “100” is the best):

const imagemin = require('imagemin');
const imageminMozjpeg = require('imagemin-mozjpeg');

(async() = > {const files = await imagemin(
      ['source_dir/*.jpg'.'another_dir/*.jpg'] and {destination: 'destination_dir'.plugins: [imageminMozjpeg({quality: 50]}}));console.log(files); }) ();Copy the code

Convert the image to WebP format

You can use the CWebP Command-line tool or the Imagemin WebP plug-in to convert images to WebP, If your project uses a build tool (such as Webpack or Gulp) then the Imagemin WebP plugin is probably the best choice, while if you only need to convert images once you can use the CLI

Using cwebp

Convert a single file, using cWebP’s default compression Settings:

cwebp images/flower.jpg -o images/flower.webp
Copy the code

Use the Imagemin Webp plug-in

We used Imagemin when we compressed the files earlier, and we still use Imagemin, but with a different plug-in. The following script will convert the files in the images directory and save them in the compressed_images directory.

const imagemin = require('imagemin');
const imageminWebp = require('imagemin-webp');

imagemin(['images/*'] and {destination: 'compressed_images'.plugins: [imageminWebp({quality: 50})]
}).then(() = > {
  console.log('Done! ');
});
Copy the code

Use WEbP images

Before:

<img src="flower.jpg" alt="">
Copy the code

After:

<picture>
  <source type="image/webp" srcset="flower.webp">
  <source type="image/jpeg" srcset="flower.jpg">
  <img src="flower.jpg" alt="">
</picture>
Copy the code

The browser checks from top to bottom which image format it supports, and if the browser does not support any of the formats listed in the Source tag, it will fall back to the image specified by loading the IMG tag.

Replace GIF animations with videos for faster page loading

Create an MPEG video

There are many ways to convert GIFs to video, FFmpeg is one of them. From the console, run the following command to convert GIFs to MP4

ffmpeg -i my-animation.gif -b:v 0 -crf 25 -f mp4 -vcodec libx264 -pix_fmt yuv420p my-animation.mp4
Copy the code

The IBX264 encoder only works with even size files, such as 320px by 240px. If the input GIF has an odd size, you can include a filter in the command to avoid FFmpeg throwing errors:

ffmpeg -i my-animation.gif -vf "crop=trunc(iw/2)*2:trunc(ih/2)*2" -b:v 0 -crf 25 -f mp4 -vcodec libx264 -pix_fmt yuv420p  my-animation.mp4Copy the code

Create a WebM video

MP4 has been around since 1999, and WebM is a relatively new file format, originally released in 2010. WebM videos are much smaller than MP4 videos, but not all browsers support WebM

To convert my-animation.gif to WebM video using FFmpeg, run the following command from the console:

ffmpeg -i my-animation.gif -c vp9 -b:v 0 -crf 41 my-animation.webm
Copy the code

In this example, the original GIF was 3.7MB, compared to 551 KB for the MP4 version and 341 KB for the WebM version

Replace GIF with video in HTML

<video autoplay loop muted playsinline>
  <source src="my-animation.webm" type="video/webm">
  <source src="my-animation.mp4" type="video/mp4">
</video>
Copy the code

Browsers don’t guess which video source is optimal, so the order of the source code is important. For example, if you specify an MPEG4 video first and the browser supports WebM, the browser will skip WebM and use MPEG4 instead. If you want to use WebM first, specify it first

Use responsive images

Using responsive images allows browsers to load different image resources on different device pixel densities or viewport widths, which is great for site performance and user experience

Specify multiple image versions and the browser will select the most appropriate size to display to the user:

The SRC attribute makes this code work in browsers that do not support srcset and SIZES. If the browser does not support these attributes, it will fall back to loading the resource specified by the SRC attribute, so the resource specified by the SRC attribute should be large enough to work properly on all devices.

The srcset property is a comma-separated list of image file names and their width or density descriptors. Write width descriptors in w units instead of px, for example, a 1024px wide image would be written as 1024W, and write density descriptors in x units. This example uses the width descriptor. 480w is a width descriptor that tells the browser that flower-small.jpg is 480px wide; 1080w is a width descriptor that tells the browser that flower-larg. JPG is 1080px wide

Sizes tells the browser how wide the image should be. However, sizes has no effect on the size of the image. You still need CSS. Sizes can be specified using a variety of units and the following are all valid sizes:

  • 100px
  • 33vw
  • 20em
  • calc(50vw-10px)

The following is not a valid size

20% (percentage cannot be used for sizes attribute)

If you want to be more fancy, you can also specify multiple slot sizes using sizes, which is suitable for sites with different viewport sizes and layouts.

<img src="flower.jpg"
  srcset="flower-small.jpg 480w, flower-large.jpg 800w"
  sizes="(max-width: 480px) 100vw, (max-width: 1024px) 50vw, 800px">
Copy the code

The value of the SIZES attribute tells the browser: Images should be 100% of the viewport width up to 480 pixels wide, 50% of the viewport width 481-1024 pixels wide, and 800 pixels wide on a 1024 pixels wide screen. These should match the width specified in the CSS

Lazy loading image

There are two ways to display an image on a web page, either by inlining the image with the IMG tag or by using the image as a background.

Lazy loading of inline images

For inline images, we have three lazy loading options that can be combined for optimal browser compatibility:

  1. Using loading = “lazy”
  2. Use the Intersection Observer API
  3. Use scroll and resize event handlers

loading=”lazy”

Chrome and Firefox both support lazy loading through the loading property. This attribute can be added to the IMG element or to the iframe element. Loading =”lazy” tells the browser to load the image immediately if it is in the viewport. If loading=”lazy” is not supported by the browser, this property will be ignored and the image will load immediately as usual.

<img 
    src='path/to/img' 
    loading="lazy"
/>
Copy the code

Intersection Observer API

To polyfill lazy loading of img elements with loading=”lazy”, we use the Intersection Observer API to check if they are in the viewport. If so, their SRC attribute will be populated with the image’s URL. Intersection Observer Not all browsers support Intersection Observer, especially IE11 and below. If cross-browser compatibility is important, be sure to read the next section, which explains how to use the less-performing but more compatible scroll and resize event handlers to lazy-load images.

Intersection Observer is simpler than code that relies on various event handlers because you only register an Observer to monitor an element instead of writing lengthy element visibility inspection code. All that’s left to do is decide what to do when the element becomes visible. We assume that lazy loaded IMG elements have the following basic markup patterns:

<img class="lazy" src="placeholder-image.jpg" data-src="image-to-lazy-load-1x.jpg" data-srcset="image-to-lazy-load-2x.jpg 2x, image-to-lazy-load-1x.jpg 1x" alt="I'm an image!">
Copy the code

You should focus on three related parts of the IMG tag:

  1. Class property, which you will use in JavaScript to select elements.
  2. SRC property, which refers to the placeholder image that appears when the page is first loaded.
  3. The data-src and data-srcset attributes, which are placeholder attributes, contain the URL of the image to be loaded when the element enters the viewport.

Now let’s look at using Intersection Observer in JavaScript to lazily load an image:

document.addEventListener("DOMContentLoaded".function() {
  var lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));

  if ("IntersectionObserver" in window) {
    let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
      entries.forEach(function(entry) {
        if (entry.isIntersecting) {
          let lazyImage = entry.target;
          lazyImage.src = lazyImage.dataset.src;
          lazyImage.srcset = lazyImage.dataset.srcset;
          lazyImage.classList.remove("lazy"); lazyImageObserver.unobserve(lazyImage); }}); }); lazyImages.forEach(function(lazyImage) {
      lazyImageObserver.observe(lazyImage);
    });
  } else {
    // Possibly fall back to event handlers here}});Copy the code

On the document’s DOMContentLoaded event, the script uses class to query all lazy elements in the DOM. If IntersectionObserver is available, a new observer is created to run a callback

Support Internet Explorer with event handlers

Use scroll, resize, and possibly orientationChange event handlers along with getBoundingClientRect to determine if an element is in view, assuming the HTML code is the same as before

document.addEventListener("DOMContentLoaded".function() {
  let lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));
  let active = false;

  const lazyLoad = function() {
    if (active === false) {
      active = true;

      setTimeout(function() {
        lazyImages.forEach(function(lazyImage) {
          if ((lazyImage.getBoundingClientRect().top <= window.innerHeight && lazyImage.getBoundingClientRect().bottom >= 0) && getComputedStyle(lazyImage).display ! = ="none") {
            lazyImage.src = lazyImage.dataset.src;
            lazyImage.srcset = lazyImage.dataset.srcset;
            lazyImage.classList.remove("lazy");

            lazyImages = lazyImages.filter(function(image) {
              returnimage ! == lazyImage; });if (lazyImages.length === 0) {
              document.removeEventListener("scroll", lazyLoad);
              window.removeEventListener("resize", lazyLoad);
              window.removeEventListener("orientationchange", lazyLoad); }}}); active =false;
      }, 200); }};document.addEventListener("scroll", lazyLoad);
  window.addEventListener("resize", lazyLoad);
  window.addEventListener("orientationchange", lazyLoad);
});
Copy the code

While this code works in almost all browsers, it has potential performance issues. Lazy loading is implemented using loading=”lazy” + Intersection Observer whenever possible, and event handlers only when broadest compatibility is a critical requirement of the application

The image is in CSS

While the IMG tag is the most common way to use images on a web page, images can also be called using the CSS background-image property. Lazy loading with Loading =”lazy” does not work for CSS background images, so if you need to lazy load background images, you need to consider other methods.

Unlike the IMG element, which loads images without regard to their visibility, after building the Document and CSS object models and the Render tree, the browser checks how CSS is applied to the Document before requesting external resources. If the browser has determined that the CSS rules for the external resource involved do not apply to the currently constructed Document, the browser does not request it. This behavior can be used to delay the loading of images in CSS by using JavaScript to determine when an element is in the viewport and then adding a class to that element to apply the style to load the background image, which results in the image being downloaded when needed, rather than at initial loading time. For example, let’s use the following code:

<div class="lazy-background">
  <h1>Here's a hero heading to get your attention!</h1>
  <p>Here's hero copy to convince you to buy a thing!</p>
  <a href="/buy-a-thing">Buy a thing!</a>
</div>
Copy the code
.lazy-background {
  background-image: url("hero-placeholder.jpg"); /* Placeholder image */
}

.lazy-background.visible {
  background-image: url("hero.jpg"); /* The final image */
}
Copy the code

Use Intersection Observer in JavaScript to check if the element is in the viewport, and then add the Visible class to the div.lazy-background element:

document.addEventListener("DOMContentLoaded".function() {
  var lazyBackgrounds = [].slice.call(document.querySelectorAll(".lazy-background"));

  if ("IntersectionObserver" in window) {
    let lazyBackgroundObserver = new IntersectionObserver(function(entries, observer) {
      entries.forEach(function(entry) {
        if (entry.isIntersecting) {
          entry.target.classList.add("visible"); lazyBackgroundObserver.unobserve(entry.target); }}); }); lazyBackgrounds.forEach(function(lazyBackground) { lazyBackgroundObserver.observe(lazyBackground); }); }});Copy the code

There are some open source libraries for lazy loading

  1. lazysizes
  2. vanilla-lazyload
  3. lozad.js
  4. yall.js
  5. react-lazyload

Lazy-loaded video

As with image elements, you can lazily load video. Videos are usually loaded using the video element. However, how the video is lazily loaded depends on the scenario in which the video is used. Here we discuss two scenarios, each requiring a different solution

Lazy loading of videos that do not need to play automatically

<video controls preload="none" poster="one-does-not-simply-placeholder.jpg">
  <source src="one-does-not-simply.webm" type="video/webm">
  <source src="one-does-not-simply.mp4" type="video/mp4">
</video>
Copy the code

The example above uses a preload property with a value of None to prevent the browser from preloading any video data. The Poster attribute provides a placeholder for the video element that will occupy space when the video loads.

Lazy loading of animated GIFs replaced with videos

While giFs are widely used, they fall short of video in many ways, especially in file size. Replacing GIF animations with the video element is not as simple as using the IMG element. GIF animation has three features:

  1. Automatically play
  2. Loop for
  3. Silent play

Use the video element to achieve this effect as follows:

<video autoplay muted loop playsinline>
  <source src="one-does-not-simply.webm" type="video/webm">
  <source src="one-does-not-simply.mp4" type="video/mp4">
</video>
Copy the code

How to lazily load GIF animations with video replacement, modify HTML as follows:

<video class="lazy" autoplay muted loop playsinline width="610" height="254" poster="one-does-not-simply.jpg">
  <source data-src="one-does-not-simply.webm" type="video/webm">
  <source data-src="one-does-not-simply.mp4" type="video/mp4">
</video>
Copy the code

I modified SRC and data-src in source, and then used JavaScript code similar to the Intersection Observer’s image-lazy-loading example:

document.addEventListener("DOMContentLoaded".function() {
  var lazyVideos = [].slice.call(document.querySelectorAll("video.lazy"));

  if ("IntersectionObserver" in window) {
    var lazyVideoObserver = new IntersectionObserver(function(entries, observer) {
      entries.forEach(function(video) {
        if (video.isIntersecting) {
          for (var source in video.target.children) {
            var videoSource = video.target.children[source];
            if (typeof videoSource.tagName === "string" && videoSource.tagName === "SOURCE") {
              videoSource.src = videoSource.dataset.src;
            }
          }

          video.target.load();
          video.target.classList.remove("lazy"); lazyVideoObserver.unobserve(video.target); }}); }); lazyVideos.forEach(function(lazyVideo) { lazyVideoObserver.observe(lazyVideo); }); }});Copy the code

When loading a video element lazily, you need to traverse all source elements and convert their data-src attribute to SRC attribute. Then you need to call the video load method to trigger the load of the video, and the media will automatically play the video according to the autoplay attribute

conclusion

Here are some strategies for optimizing images and videos on your website:

  1. Use vector images whenever possible: Vector images appear resolution-independent, making them ideal for multiple devices and high resolutions
  2. Shrink and compress SVG assets: The XML tags generated by most drawing applications often contain unnecessary metadata that can be removed, make sure your server is configured to apply GZIP compression to SVG assets.
  3. Use WebP format whenever possible when using bitmaps: WebP images are usually much smaller than older format images.
  4. Compressed bitmaps: Don’t be afraid to degrade the quality, the results are usually very good, and the file size reduction is significant.
  5. Remove unnecessary image metadata: Many bitmaps contain unnecessary metadata about the image, such as geographic information and camera information
  6. Use responsive images: Let the browser load different image resources at different device pixel densities or viewport widths
  7. Use videos instead of GIFs: GIF files are too large and don’t work well
  8. Lazy loading of images and videos: Reduce the initial load on your site and let users load the images and videos they need to see
  9. Place the picture on CND