Medium 前端技术 - 图片加载模式

2,082 阅读4分钟
原文链接: jmperezperez.com

October 19 2015

Recently, I was browsing a post on Medium and I spotted a nice image loading effect. First, load a small blurry image, and then transition to the large image. I found it pretty neat and wanted to disect how it was done.

A screenshot of a blurry placeholder while the image is loaded

Medium's technique

To see how image loading works in Medium, it is best to see a demo:

I have performed a WebPageTest test against this page on Medium where you can see how it loads too. And if you want to see it by yourself, open Medium's post in your browser, disable the cache and throttle the response so it takes longer to fetch the images and you can see the effect.

Here is what is going on:

  1. Render a div where the image will be displayed. Medium uses a

    with a padding-bottom set to a percentage, which corresponds to the aspect ratio of the image. Thus, they prevent reflows while the images are loaded since everything is rendered in its final position.

  2. Load a tiny version of the image. At the moment, they seem to be requesting small JPEG thumbnails with a very low quality (e.g. 20%). The markup for this small image is returned in the initial HTML as an , so the browser starts fetching them right away.

  3. Once the image is loaded, it is drawn in a . Then, the image data is taken and passed through a custom blur() function You can see it, a bit scrumbled, in the main-base.bundle JS file. This function is similar, though not identical, to StackBlur's blur function. At the same time, the main image is requested.

  4. Once the main image is loaded, it is shown and the canvas is hidden.

All the transitions are quite smooth, thanks to the CSS animations applied.

Markup

A bird's eye view of the markup for an image:

And a concrete example, so you see what goes in those tags:

 name="7012" id="7012" class="graf--figure graf--layoutFillWidth graf-after--h4">
   class="aspectRatioPlaceholder is-locked">
     class="aspect-ratio-fill" style="padding-bottom: 66.7%;">
     class="progressiveMedia js-progressiveMedia graf-image is-canvasLoaded is-imageLoaded" data-image-id="1*sg-uLNm73whmdOgKlrQdZA.jpeg" data-width="2000" data-height="1333" data-scroll="native">
       src="https://cdn-images-1.medium.com/freeze/max/27/1*sg-uLNm73whmdOgKlrQdZA.jpeg?q=20" crossorigin="anonymous" class="progressiveMedia-thumbnail js-progressiveMedia-thumbnail">
         class="progressiveMedia-canvas js-progressiveMedia-canvas" width="75" height="47">
         class="progressiveMedia-image js-progressiveMedia-image __web-inspector-hide-shortcut__" data-src="https://cdn-images-1.medium.com/max/1800/1*sg-uLNm73whmdOgKlrQdZA.jpeg" src="https://cdn-images-1.medium.com/max/1800/1*sg-uLNm73whmdOgKlrQdZA.jpeg">
         class="js-progressiveMedia-inner">
    
  

Note that the actual image sizes requested depend on the device.

An attempt to reproduce the effect

I have prepared this CodePen where I have implemented the same effect, though using CSS filters for the blur instead of a canvas (see below more info about this variant).

Here is a demo:

See the Pen Medium loading image effect reproduced by José Manuel Pérez (@jmperez) on CodePen.

You can see it better in full screen. I recommend that you use network throttling and disable cache to notice the full animation.

Is it worth it?

Clearly, there is a lot of things going on to be able to render an image this way, and it can be discouraging to do something similar on your site. A few years ago it would have been impossible to do this animations and blur effects in a performant way, but the truth is that most of the times the latency is the bottleneck, not the device capabilities, and we can play with these visual explorations.

Having full control of the loading of images has some advantages:

  • Lazy loading. Using JS for making the requests allows them to be in control of what images are requested. While all the small thumbnails are requested, the large images are only requests when they are within the viewport.

  • Better placeholder. The thumbnails are very small, barely 2kB, which combined with the blurry effect allows for a better placeholder than a solid colour, without sacrificing payload.

  • Tailored image sizes. Medium serves different images sizes depending on the device that makes the requests, which optimises the weight of the page.

Variants

Before finding out about this technique, I thought of using a similar approach for a site I'm working on.

Inlining image data

Instead of making a request for the small thumbnails, it is possible to inline them using data URIs. This increases the size of the HTML, but accelerates the rendering of the placeholder, which is immediate one the markup is downloaded. The blur effect allows these images to be really small. I did some tests with 0.5kB size images, and the result was similar to using a 4x larger image.

Blur effect

By default, when a browser renders a small image scaled up, it applies a light blur effect to smooth the artifacts of the image. The effect can also be turned off for images like QR codes.

[...]the browser would render it in a way that didn’t make it look blocky[...] from Google Developers.

This works both in Chrome, Safari and Firefox (I haven't tried on IE yet), though the smoothing effect is more prominent in Chrome. Here is a demo, but you can see it better in full screen:

See the Pen Blur effect when scaling up a small image by José Manuel Pérez (@jmperez) on CodePen.

Note how the artifacts are smoothen. Keep in mind that the image is only 27px wide and has very low quality, which should result in an awful scaled-up version, but it isn't. If the above effect is enough for you, then you don't need to use more complicated alternatives.

The blur effect can also be achieved using CSS Filter Effects. Its support is quite wide aside from IE. I'm pretty sure Medium tried this before going to a canvas solution, which feels far too over-engineered, but for some reason they decided not to use it.

See the Pen Blur effect using CSS blur() filter on a small image by José Manuel Pérez (@jmperez) on CodePen.

The advantage of this technique is that you can easily tweak how much blur you want and everything is achieved using CSS.

Other ways of improving placeholders: Google Images Search

A simpler technique is used by Google Search when searching for images from a smartphone:

Searching on Google Images from the phone ↑ Google Images Search shows a solid background as placeholder (left image is while loading, right when already loaded).

They pick a colour (maybe the dominant colour of the picture?) and they use it a solid colour background. It gives the user the feeling that images loads faster.

Conclusion

As our pages load more and more images, it is good to think of their loading process on our pages, since it affects performance and user experience.

If you are generating several thumbnail sizes for your images, you can experiment using a very small one to use it as the background while the final image loads.

And if you are interested in other image tips and tricks, check out Image optimization: Lossy, lossless and other techniques.