Monday 27 January 2014

Adaptive images: solving the responsive image problem

Responsive design isn’t just a change in layout or the use of media queries here and there, it is a mind-state and an action that has clear meaning.
Responsive design is essentially saying that we care more about the content than we have in the past. In fact, we care so much that we will even optimize content to be read and viewed on devices that haven’t been launched yet.
In essence, we are trying to present information as clearly as possible and be as efficient as possible all at the same time. Here’s a common misconception; mobile first means designing as though your entire site revolves around the mobile phone. That isn’t quite accurate. Mobile first simply means to design for the simplest experience first, which often leads us to cut out overheads that we are experiencing or may experience in the future.
In the world of design; quick decisions; responsiveness; and creative content we need to be vigilant first and foremost. And being vigilant comes down to knowing when we have a problem in our responsive designs, and how to fix it. Today, we are doing just that.
Responsive images have been a tough topic for years now, as there have typically been more than one ‘hack-around’ way to make your images responsive. Let’s go through this topic from the ground up, starting with how we used to do it.

The past

The Boston Globe website is a classic example of liquid design.
Before we go any further we need to go over how a web page behaves so we can talk about how it works. A quick rundown: the HTML is loaded sequentially, then resources are requested immediately as they are encountered, scripts are then executed immediately and then all cookies are sent with HTTP requests.
The process of request/pulled/fetch/etc. has placed a bit of a limitation on how creative we can get with these methodologies. Though, that certainly hasn’t stopped people in the past. Here are a few ways they’ve gotten around this.

Replacing the “src” attribute

We can use javascript to rewrite the “src” attribute, so that it pulls and replaces an image based on browser size, which appears to work just fine. This has been what a lot of people have used in the past. The problem with this is that it uses a double HTTP request. First it pulls the original image, and then it replaces it with the javascript’d image. You are essentially doing more than you would have done if you did nothing at, all though it has the appearance of working.
Are there workarounds for this? There are, indeed!
There is a method a lot of people use where we put a 1px gif image in the site instead of the actual image so that instead of fetching two images for the price of two, you essentially get two for the price of one – but that isn’t ideal either. In this case you are still making two HTTP requests.
This also means you are relying on javascript for all images. That is tricky, because mobile carriers can mess with javascript, any number of other things can break javascript, and a surprising number of website users disable it deliberately.

Noscript

Another method that has gained some popularity is to use the “noscript” tag for mobile images and then use javascript to swap it out for a higher resolution image. This seemed to take the community by storm a while back due to the ability to swap from mobile up to high-rez versions, and that really coincided with the wide misinterpretation of ‘mobile first’ I mentioned above. This doesn’t work in IE. For an Internet Explorer workaround you will need to write the following:
<script><!--mce:0--></script>
<img src=""mobile.jpg"" alt="" /> <!----->
But the problem with that is that now it doesn’t work in the popular web browser Firefox. So what we have to do is:
<script><!--mce:1--></script>
    <noscript>
    <img src=""mobile.jpg"" alt="" />
    </noscript>
As you can see, that isn’t very simple and that certainly isn’t very robust. There really isn’t a way to do it cleanly or simply at all. In fact, a lot of the people that work in responsive images have been trying to solve these problems for years now, and really haven’t gotten too far with it.
Typically what they have done is used some sort of javascript to work through the problem and accepted the double http request as a necessary evil.

Server side solution?

The typical server side solution for this is to use javascript to replace the “src” with the HTML5 “-data-highsrc”, and store the browser size in a cookie. Though, it then sends the same multiple HTTP requests as before.
The reason people liked this method was that they felt it was more secure given they were storing the browser size in a cookie, and they felt there was less margin for error. In reality though, that’s not accurate. Here are a few reasons why this method isn’t as great as the other methods listed thus far. It only allows the fetching of big and small images, it doesn’t deal with device orientation changes, and it breaks badly because now browsers pre-fetch images. Also a big fallback is that sometimes cookies aren’t set fast enough resulting in desktop’s getting images intended for mobiles.
Because of all this, namely a failure of suitable options on the server and client-side, we need a new solution.
And this is right where the adaptive images method steps in.

The adaptive images method

Adaptive images is the true solution to this whole conundrum. It is literally as easy as a drag and drop onto your server and you are all done. This adaptive method uses one htaccess file, one php file and a single line of javascript, and that’s it.
You simply drag the htaccess and php file to your root directory and add the javascript to the head of your index file and you are complete. Nothing else to even worry about. Now, it does offer a ton of customization, but we will get into that near the end.
For now let’s jump right into the beginning of the Adaptive Method.

The goals

First let’s identify the goals of the project. The creator of adaptive images, Matthew Wilcox, has identified these as his goals for this solution:
  • Has to be easy to set up and use.
  • Must be as low maintenance as possible.
  • Has to work with existing content, no markup edits or custom CMS needed.
  • Must allow for multiple versions.
  • Must work with design breakpoints and not device breakpoints.
  • Must be easily replaced when a superior solution arrives.
And these goals for this project all rely on the assumption that the
<img src=""#"" alt="" />
tags in your site are already using the highest resolution image, which in my opinion is reasonable assumption. Typically we will have the best images on our site already, as I know very few web designers that put their best images on phone versions and their worst on the web. That is pretty self explanatory too.

How it works

We are just about to dive into the code, but before we do let’s talk about how it works on a higher level. Simply put, the javascript detects the largest screen dimensions available on that device and stores it in a cookie. The .htaccess file then points certain requests to adaptive-images.php, and then based on those rules the PHP file does some processing. Inside that processing is where the real magic happens, and by all means I’d recommend everyone reading this check out the PHP file. It is the most beautifully written PHP I have seen in years. It is definitely a must-see.
Now let’s move onto break down the specifics of how these files work, and interplay with one another. Here we will be discussing everything you get when you download the package from the adaptive-images site.

The javascript code

The javascript code you will need to copy is this:
<script>document.cookie='resolution=
'+Math.max(screen.width,screen.height)+'; path=/';</script>
And it has to go before any other javascript in your head section. Also worth noting is that if you’d like to take advantage of the retina display on any of the more recent Apple products then you can use the following javascript line:
<script>document.cookie='resolution=
'+Math.max(screen.width,screen.height)+("devicePixelRatio" in window ? ","+devicePixelRatio : ",1")+'; path=/';</script>
As you can see, that last line is very similar and the only difference is that it will send higher-resolution images to such devices that allow it — be aware that it will mean slower downloads for Retina users, but better images of course.
Take note that this still needs to be the first javascript in your head section.

The .htaccess file

A .htaccess file is simply a glorified directory management utility, and if you already have a website that you are considering using adaptive images on then you more than likely already have an .htaccess file, so what we will need to do is add some contents. Simply open it (it is always located in the root directory of your site), and add this:
<IfModule mod_rewrite.c>
  Options +FollowSymlinks
  RewriteEngine On

  # Adaptive-Images ----------------------------------------

  # Add any directories you wish to omit from the Adaptive-Images process on a new line, as follows:
  # RewriteCond %{REQUEST_URI} !some-directory
  # RewriteCond %{REQUEST_URI} !another-directory

  RewriteCond %{REQUEST_URI} !assets

  # Send any GIF, JPG, or PNG request that IS NOT stored inside one of the above directories
  # to adaptive-images.php so we can select appropriately sized versions
  RewriteRule \.(?:jpe?g|gif|png)$ adaptive-images.php

  # END Adaptive-Images ----------------------------------------
</IfModule>
Now, the interesting part of this is that you really don’t need to make any changes at all.
Typically sites will want all of their images to be responsive and play nice with all form factors so due to that you really don’t need to exclude anything. If you do want or need to, there is the option there but remember you want to be responsive and progressive. The .htaccess file here is the perfect for this project, and serves as a key in this whole process so without it you really can’t use this method. As a result you must make sure you don’t forget this, or add it if you don’t have one.

The PHP file

All you have to do with this one is drag and drop it into your root directory, and it will take care of everything else. There is a small customizable section as you can see here:
/* CONFIG ------------------------------ */

$resolutions = array(1382, 992, 768, 480); // the resolution break-points to use (screen widths, in pixels)
$cache_path = "ai-cache"; // where to store the generated re-sized images. Specify from your document root!
$jpg_quality = 80; // the quality of any generated JPGs on a scale of 0 to 100
$sharpen  = TRUE; // Shrinking images can blur details, perform a sharpen on re-scaled images?
$watch_cache = TRUE; // check that the adapted image isn't stale (ensures updated source images are re-cached)
$browser_cache = 60*60*24*7; // How long the BROWSER cache should last (seconds, minutes, hours, days. 7days by default)

/* END CONFIG ------ Don't edit anything after this line unless you know what you're doing -------------- */
As it says about the rest of the script, if you don’t know what you’re doing then why not just leave it alone? In case you like tinkering let’s just shed some light here.
$resolutions are the screen widths we’ll work with. In the default it will store a re-sized image for large screens, normal screens, tablets, phones, and tiny phones.
$cache_path if you don’t like the cached images being written to that folder, you can put it somewhere else. Just put the path to the folder here and make sure you create it on the server.
$sharpen will perform a subtle sharpening on the rescaled images. Usually this is fine, but you may want to turn it off if your server is very very busy.
$watch_cache if your server gets very busy it may help performance to turn this to FALSE. It will mean however that you will have to manually clear out the cache directory if you change a resource.
Now that you know all about the customization you may be curious, just what does the PHP file do exactly? Well let’s walk through it step by step:
  • It reads the cookie and fits the result into breakpoints that match the CSS breakpoints
  • It checks its own cache directory to see if a version of the requested file exists at that breakpoint size.
  • If it does, then it compares the dates on that and the origin to ensure the cache version is not stale.
  • If it doesn’t exist cached; then it creates a rescaled image only if the source image is larger than the breakpoint size. Then it caches that for future use.

The ai-cookie.php file

You also get this ‘ai-cookie.php’ file in your folder when you download the adaptive images package, but this can actually be deleted as it has to do with an alternate method for detecting users’ screen size. The creator of adaptive images recommends you delete this and go with the standard method.
And that’s about it for the contents of that package. Now, do make sure you take a look at all the files you are popping into your site and double check you are using best practices with media-queries. Also, be sure to ask questions if you have any on this content or media-queries in general as I love talking about that sort of thing. Now let’s summarize what we have here.

In summation:

It is certainly a fascinating system, and one that I foresee being in use for years to come. Firstly, what exactly can I customize with this system as a whole?
With this system you can:
  • Set breakpoints to match your CSS.
  • Specify where you want the cache folder.
  • Set the quality of the generated JPGs.
  • Set how long browsers should cache the image for.
  • Subtly sharpen generated images.
  • Alternate javascript to detect high DPI devices.
In the future I’d also all love it to detect bandwidth on a system, instead of device size or browser width. Because that is the real key in deciding what image to send where, but as of now there is no feasible way to do that.

No comments:

Post a Comment