Technical Article => Programming => Algorithm

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"> <img class="progressiveMedia-noscript js-progressiveMedia-inner" src="https://cdn-images-1.medium.com/fit/t/1600/480/1*dZnfeZiXxf2BgN3VSQuOlA.jpeg"> </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?

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. 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. 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. 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:

Create a div, make sure it has the same ratio and size as the real image. Fill it with a light background color Load the small image first and adds the blur effect Start to load real image when small image is being loaded and rendered 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

attribute of each 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