Loading images progressively using Gaussian blur

Summary

Medium employs a progressive image loading strategy to enhance user experience by displaying a blurred placeholder while high-resolution images load. This involves first loading a small, low-quality version of an image, applying a Gaussian blur effect, and rendering it. Concurrently, the full-resolution image is fetched, and upon completion, it smoothly replaces the blurred placeholder. Implementing this technique requires an HTML structure that maintains aspect ratio, CSS for styling and blur effects, and JavaScript to manage image loading, dynamic padding-bottom calculation, and visibility transitions using onload events.

The popular online publishing platform Medium has adopted an impressive image loading mechanism -- pure color - blur image loading - real image loading.

Since images on Medium usually have high definition, it takes much time to load an image and hence brings a bad user experience if rendering the image after it's completely downloaded. The solution Medium comes out is to preload an small image when the real image is being loaded. 

On Medium, the HTML code will have below pattern for every page.

<figure name="512a"
        id="512a"
        class="graf--figure graf--layoutCroppedHeightPreview graf-after--h3"
>
    <div class="aspectRatioPlaceholder is-locked">
        <div class="aspectRatioPlaceholder-fill"
             style="padding-bottom: 30%;"
        ></div>
        <div class="progressiveMedia js-progressiveMedia graf-image is-canvasLoaded"
             data-image-id="1*dZnfeZiXxf2BgN3VSQuOlA.jpeg"
             data-width="3600"
             data-height="3600"
             data-scroll="native"
        >
            <img src="https://cdn-images-1.medium.com/freeze/fit/t/60/18/1*dZnfeZiXxf2BgN3VSQuOlA.jpeg?q=20"
                 crossorigin="anonymous"
                 class="progressiveMedia-thumbnail js-progressiveMedia-thumbnail"
            >
            <canvas class="progressiveMedia-canvas js-progressiveMedia-canvas"
                    width="75"
                    height="22"
            ></canvas>
            <img class="progressiveMedia-image js-progressiveMedia-image"
                 data-src="https://cdn-images-1.medium.com/fit/t/1600/480/1*dZnfeZiXxf2BgN3VSQuOlA.jpeg"
                 src="https://cdn-images-1.medium.com/fit/t/1600/480/1*dZnfeZiXxf2BgN3VSQuOlA.jpeg"
            >
            <noscript class="js-progressiveMedia-inner">
                &lt;img class="progressiveMedia-noscript js-progressiveMedia-inner" src="https://cdn-images-1.medium.com/fit/t/1600/480/1*dZnfeZiXxf2BgN3VSQuOlA.jpeg"&gt;
            </noscript>
        </div>
    </div>
</figure>

For every image, the above code block will be created. It's a bit lengthy but these all are combined together to achieve the smooth image loading experience showing in front of you.

But at the end how does this code snippet work?

  1. Rending a div container, this container will be used to show the real image loaded. It will have a padding-bottom attribute set which will guarantee its ratio and size is the same as the real image ratio and size. This is to avoid page re-rendering.
  2. Use an img element to load an image which only has 10% - 20% quality of the real image. This is an image with poor quality and small size so that it can be loaded immediately.
  3. Once the small image is loaded, the canvas will be used to render the image and adds bllur effect with Gaussian blur algorithm. Meanwhile the real image will be loaded.
  4. When the real image is loaded, the image will be rendered and the canvas will be hidden.

This same effect can be accomplished by our own through below steps:

  1. Create a div, make sure it has the same ratio and size as the real image. Fill it with a light background color
  2. Load the small image first and adds the blur effect
  3. Start to load real image when small image is being loaded and rendered
  4. Start to render real image and hide small image when real image is loaded

Not that complicated, right? In fact, the small and real image URLs can be set in the data attributes. The HTML may look like.

<figure name="blur"
        class="blur-img-container"
        data-real-width="1174"
        data-real-height="670"
        data-src="images/sm2.jpeg"
        src="https://cdn-images-1.medium.com/max/2000/1*0WwtDkE1q6HGZwD6Kn9SuQ.jpeg"
></figure>

The meaning of each attribute is 

  • data-real-width : Real image width
  • data-real-height : Real  image height
  • data-src : Small image URL
  • src : Real image URL

The corresponding CSS may look like

.blur-img-container {
    position: relative;
    background: #eeeeee;
    background-size: cover;
    overflow: hidden;
}

.blur-img-container img {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    transition: all 0.4s ease-in-out;
}

.blur-img-container .thumb-loaded {
    opacity: 1;
    filter: blur(10px);
    transform: scale(1);
}

.blur-img-container .large-loaded {
    opacity: 1;
}

.blur-img-container .thumb-hidden {
    opacity: 0;
}

The main focus should be the JavaScript code which drives the whole process. 

  • Dynamically set the padding-bottom attribute of each div container
  • Control HTML style and progress using load event of image

The padding-bottom can be calculated with below

elem.style.paddingBottom = `${(realHeight / realWidth) * 100}%`;

The onload event can be implemented as below

let thumb = new Image();
thumb.src = thumbSrc;
thumb.onload = () => {
    setStyle(thumb, 'thumb-loaded');
};
elem.appendChild(thumb);

let realImg = new Image();
realImg.src = lgSrc;
realImg.onload = () => {
    setStyle(realImg, 'large-loaded');
    setStyle(thumb, 'thumb-hidden');
};

elem.appendChild(realImg);

With above code, the basic skeleton of this mechanism is completed. The whole code and example can be found at blur-image.

Source : https://segmentfault.com/a/1190000006743512

JAVASCRIPT ALGORITHM GAUSSIAN BLUR MEDIUM BLUR IMAGE

  RELATED

  COMMENT

1
Anonymous
Aug 22, 2018 at 8:40 am

Nice tutorial ,just give this jquery plugin a try, progressive image loader with with progressive bar .

https://github.com/galactas-source/bleach