Responsive Images With Picturefill And Imagefly

Responsive images is a challenging part of responsive web design. You would think that showing an image sized appropriately to the screen it's displayed on would be pretty straightforward, but as soon as you start coding it, you'll discover a number of problems.

Some of the issues you'll likely run into include:

There have been a number of solutions proposed and even a formal requirements document drafted to help guide the solutions. The proposed solutions span markup based proposals such as <picture>, srcsets and src-N, HTTP protocol changes such as client hints and even new image file formats.

A Working Example

We're going to build a simple, responsive layout that solves the issues mentioned above. We'll start out with basic resolution-switching use case and expand it to support the art direction problem. To address some of the performance and image sprawl issues, we'll use Imagefly, a hosted, image processing service, to generate our web optimized images on-demand.

The <picture> Element

One of the earliest proposals was the <picture> element. <picture> resembles the <video> element and allows you to specify multiple <source> images attached to media queries. Browsers that implement the picture element would load the appropriate source depending on the browsers screen width, display density, connection type or user preferences.

Picturefill is a small, open-source, Javascript library that implements the <picture> element. The examples below will use this library.

Resolution Switching Example

This first example shows how the <picture> element is structured and how it works with Picturefill. Picturefill chose to use <span> elements to mimic the the actual <picture> and <source> elements.

We're using the same images that Jason Grigsby used to discuss a framework for responsive images. We've setup an Imagefly source to load images from the Barack Obama at Chrysler flickr photostream.

Here's the code for a basic resolution switching example:

 1 <!DOCTYPE html>
 2 <html>
 3     <head>
 4         <meta charset=utf-8 />
 5         <meta name="viewport" content="width=device-width,initial-scale=1">
 6         <script src="/js/matchmedia.js"></script>
 7         <script src="/js/picturefill.js"></script>
 8         <style>
 9             body { font-family: sans-serif }
10             img {  max-width: 100% }
11         </style>
12     </head>
13     <body>
14         <span data-picture data-alt="Barack Obama at the Chrysler Plant">
15             <span data-src="http://demo.imagefly.io/w_320/https://farm3.staticflickr.com/2494/5795228030_ddbd327d13_o.jpg"></span>
16             <span data-src="http://demo.imagefly.io/w_600/https://farm3.staticflickr.com/2494/5795228030_ddbd327d13_o.jpg" data-media="(min-width: 600px)"></span>
17             <span data-src="http://demo.imagefly.io/w_800/https://farm3.staticflickr.com/2494/5795228030_ddbd327d13_o.jpg" data-media="(min-width: 800px)"></span>
18             <span data-src="http://demo.imagefly.io/w_1000/https://farm3.staticflickr.com/2494/5795228030_ddbd327d13_o.jpg" data-media="(min-width: 1000px)"></span>
19 
20             <!-- Fallback content for non-JS browsers. Same img src as the initial, unqualified source element. -->
21             <noscript><img src="http://demo.imagefly.io/w_320/https://farm3.staticflickr.com/2494/5795228030_ddbd327d13_o.jpg" alt="Barack Obama at the Chrysler Plant"></noscript>
22         </span>
23     </body>
24 </html>

Check out the live Demo. Try resizing your browser and viewing the network connections tab in the console to see the various images loaded.

How does this work?

Picturefill determines which data-src to load based on the environment. The data-media attributes define the media queries to describe the breakpoints for the design. The data-src is the URL for the image to load. Because the data-src reference the image URL, the browser pre-loader does not load them immediately. Picturefill takes care of loading the appropriate image so that only one image is loaded.

Each image is prefixed to load through Imagefly which returns a resized and web-optimized image. We're only using a single high-resolution master image to generate the smaller images. These images are also served over the Imagefly CDN so the resizing does not happen on every request.

Art Direction Example

The previous example shows what happens when you use the same image and scale it down to a smaller screen. As Jason Grigsby pointed out, at the smallest resolution, it's hard to make out who's in the picture or that he's even at the automotive plant.

Instead, we'd like to crop his face and body. We could create another image manually and change the data-src for that breakpoint but that's another image to manage. If we account for device pixel ratios, we'll have even more images to create and optimize.

Alternatively, we can specify a crop region using the Imagefly API and still only manage a single source image.

This updated code uses a cropped region at lower resolutions. We're still using the same source image and getting the benefits from the first example. This example doesn't account for high DPI (retina) displays but you can support them using HD Media Queries with picturefill.

 1 <!DOCTYPE html>
 2 <html>
 3     <head>
 4         <meta charset=utf-8 />
 5         <meta name="viewport" content="width=device-width,initial-scale=1">
 6         <script src="/js/matchmedia.js"></script>
 7         <script src="/js/picturefill.js"></script>
 8         <style>
 9             body { font-family: sans-serif }
10             img {  max-width: 100% }
11         </style>
12     </head>
13     <body>
14         <span data-picture data-alt="Barack Obama at the Chrysler Plant">
15              <span data-src="http://demo.imagefly.io/c_1080x560-1600x1160,w_320/https://farm3.staticflickr.com/2494/5795228030_ddbd327d13_o.jpg"></span>
16              <span data-src="http://demo.imagefly.io/c_1080x560-1600x1160,w_600/https://farm3.staticflickr.com/2494/5795228030_ddbd327d13_o.jpg" data-media="(min-width: 400px)"></span>
17              <span data-src="http://demo.imagefly.io/w_800/https://farm3.staticflickr.com/2494/5795228030_ddbd327d13_o.jpg" data-media="(min-width: 800px)"></span>
18              <span data-src="http://demo.imagefly.io/w_1000/https://farm3.staticflickr.com/2494/5795228030_ddbd327d13_o.jpg" data-media="(min-width: 1000px)"></span>
19 
20             <!-- Fallback content for non-JS browsers. Same img src as the initial, unqualified source element. -->
21             <noscript><img src="http://demo.imagefly.io/c_1080x560-1600x1160,w_320/https://farm3.staticflickr.com/2494/5795228030_ddbd327d13_o.jpg" alt="Barack Obama at the Chrysler Plant"></noscript>
22         </span>
23     </body>
24 </html>

And here's the live Demo.

What Have We Accomplished

Using Picturefill and Imagefly, we've used a single source image to create a responsive layout that only loads a single, web-optimized image that is sized to the screen displaying it. We've also created these images without requiring any backend server code to install or additional images to maintain and optimize. As an additional benefit, we've improved our page loading time by offloading the serving of images to Imagefly's CDN.

While there is no standard solution to responsive images currently, there are good ones available now that are compatible with future changes. These example have shown that Picturefill can address many of the frontend issues and server-based solutions such as Imagefly can simplify the backend issues.

comments powered by Disqus