Quantcast
Channel: Hacker News
Viewing all articles
Browse latest Browse all 10943

SVG Retina Image Demo

$
0
0

Comments:"Use SVG to Handle Retina Image Serving"

URL:http://thepenzone.com/svg-image/


How it works:

For this method, we make a single 'images.svg' file containing named references to each of the site's images, as well as a viewBox setting describing their dimensions. For the purpose of this example, the images must be in the same directory as the images.svg file, and the filenames must match these image reference names, plus '-1x.png' or '-2x.png' for the regular and double-resolution versions. Here's what the svg looks like:

<?xml version="1.0" encoding="utf-8"?><svg id="images" class="image" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><title>SVG Retina Image Demo</title><desc> Original by @erikdahlstrom - http://jsfiddle.net/cSEFk/ Modified by @tpenzer - http://thepenzone.com/svg-image/</desc><defs><style> svg .image { display: none } svg .image:target { display: inherit }</style></defs><script type="application/ecmascript"> <![CDATA[ function generateImage(evt) { var container = evt.target; var image_name = container.getAttributeNS(null, 'id'); // split the viewBox string at every space, yielding an array of x, y, width and height values var view_box = container.getAttributeNS(null, 'viewBox').split(/s+/g); // create a new image element and set its attributes var image = document.createElementNS('http://www.w3.org/2000/svg', 'image'); image.setAttributeNS(null, "x", view_box[0]); image.setAttributeNS(null, "y", view_box[1]); image.setAttributeNS(null, "width", view_box[2]); image.setAttributeNS(null, "height", view_box[3]); // for retina displays, set image href to {image_name} + {2x filename key}, else {1x filename key} if (window.devicePixelRatio > 1) { image.setAttributeNS('http://www.w3.org/1999/xlink', "href", image_name + "-2x.png"); } else { image.setAttributeNS('http://www.w3.org/1999/xlink', "href", image_name + "-1x.png"); }; //add the new image element to the container svg container.appendChild(image); } ]]> </script><svg id="logo" class="image" viewBox="0 0 149 74" onload="generateImage(evt)"></svg><svg id="promo" class="image" viewBox="0 0 200 150" onload="generateImage(evt)"></svg></svg>

There are two image references in the SVG, "logo" and "promo", as well as a viewBox entry describing their dimensions, highlighted in green at the bottom. There are images with filenames "logo-1x.png", "logo-2x.png", "promo-1x.png" and "promo-2x.png" in the same directory as the "images.svg" file. The javascript code will use the ID names, filename keys ("-1x.png" and "-2x.png") and viewBox setting to create SVG image elements upon loading, generating a path to the png image based on these names combined with the detected pixel ratio. The parts highlighted in green are the only bits that should need modification for various uses. These images can then be rendered as such:

<object type="image/svg+xml" width="149" height="74" data="images/images.svg#logo"><img src="images/images.svg#logo" width="149" height="74" /></object><object type="image/svg+xml" width="200" height="150" data="images/images.svg#promo"><img src="images/images.svg#promo" width="200" height="150" /></object>

What's going on here?

Since the image element is created upon loading the image, only a single request is made for the desired image resource. We needed to put an "img" element inside the "object" element in order for Chrome to render the image without reloading the page, and to support older browsers. This will make browsers send multiple requests for the "images.svg" file, which is tiny, but still wastes resources. You can make browsers cache this file to minimize requests by adding a "manifest" attribute to your page's html element, as such:

<html manifest="myCache.appcache">

Then, you can add a file, in this example named "myCache.appcache", with the following contents:

CACHE MANIFEST /path/to/images.svg NETWORK: *

This will force browsers to cache the svg file locally, and request it from their local cache rather than your server. Note that this will cause Firefox to prompt users on whether they'd like your site to be able to store data on their computer.

So what are the pitfalls?

  • Image filenames must conform to strict templates, and they must share the same format
  • This approach won't work at all if the user has Javascript disabled
  • The image generating JS function is run for all images in each svg request
  • Browser compatibility seems wide, but there are likely to be some issues I haven't discovered

Note that you can easily remove the javascript requirement if you don't mind your retina users making two image requests, by including image elements referencing their respective 1x assets in the images.svg file by default, and then modifying their href attributes for retina users with javascript.

The issue of the Javascript image generator being run for all images on each SVG load is due to the way in which we get the element in which to generate the image. This will get worse as more images are added to the file, though thanks to browser caching, no extra requests are made for image assets, so the problem is mainly client performance. Ideally, the onload event would likely be moved to the outermost SVG element, and it should only be run for the target image. If anyone has any ideas how this might be done, I'd love to hear them.


Viewing all articles
Browse latest Browse all 10943

Trending Articles