How to Setup

Note I: The code examples used here are in React and Sass, but the overall concept is easily replicable in any other framework or in vanilla JS, together with CSS. Note II: The examples presented here (code and app) are available on Github and on live.

First things first

Before analysing any strategy, we need to setup the application to proper do progressive image loading. Two things which all strategies have in common is the image transition after loading the original picture and how/when to trigger the original image loading.

Transition Animation

For the transition, is very simple, we can use the placeholder filling all its container and, after finish the loading, add the original image with absolute position below the placeholder. Then use a CSS transition on the opacity of placeholder and voila!

I’ve made a React component to do that, but this idea is also simple to recreate with vanilla JS:

With the component ready, we just need to setup the CSS transition on the element. When the img.thumb receive the “hide” class, it will trigger the CSS transition, making it go from 1 to 0 opacity:

Note, we scale the image by 10%, as the blur filter makes the borders partially transparent, we can hide this effect by clipping it (only for the LQIP strategy).

Triggering the load with the Intersection Observer

For the lazy loading part we need to check whenever the image is being displayed to the user (if image is inside the viewport). For that I’ve used the IntersectionObserver API. Still an experimental Web API, but you can use a polyfill to run “safely” in your application or, as the last resort, just fallback to no IntersectionObserver and do load the image on the document ready state.

The usage isn’t so hard, but for the example I’ve made a register function using Observables (RxJS) and a React High Order Component (HOC) to pass the property intersecting to the observed components.

First I’ve just made a small function that register the scroll area and exposes an Observable of the isIntersecting value from that entry:

And then the High Order Component register it sub component and pass, through the property API, all the intersection changes (true if is on the viewport, false if isn’t).

Finally, we change the previously demonstrated ProgressiveImageLoading component to trigger the loading not on the componentDidMount method, but on the componentWillReceiveProps. After doing this, we encapsulate it using the HOC we just created:

export default withIntersectionObserver(ProgresiveImageLoading);

Strategies

After all the setup we’re now able to use different strategies for the placeholders. Each one have their good/bard parts, but I’ll try to clarify which one is the best of each occasion on my usage experience.