On a recent project, we needed to come up with an approach for supporting retina images. CSS can be used to handle displaying standard, or retina images based on pixel ratio and device, but this doesn’t solve the need to handle images that are served from Sitecore (specifically, Sitecore 7.2). We also wanted to avoid the user having to upload both a standard version of the image, and a retina version of the image, to keep the authoring experience as simple and seamless as possible.

To solve this problem, we used a combination of Sitecore’s image scaling functionality, the new srcset attribute, and respimage–a responsive images polyfill.

In order to ensure that the author only needed to upload one image, we set the expectation that they would only be uploading the retina version of the image. This allowed us to leverage Sitecore’s image scaling capabilities to scale the image in half, which would then give the standard version of the image.

First, we created a Sitecore extension method to return an image url at a given scale:

public static string GetImageUrl(this Item item, string fieldName, float scale)
{
	Assert.ArgumentNotNull(item, "item");
	Assert.ArgumentNotNull(fieldName, "fieldName");

	string imageUrl = string.Empty;
	ImageField image = item.Fields[fieldName];

	if (image != null && image.MediaItem != null)
	{
		MediaItem imageItem = new MediaItem(image.MediaItem);
		MediaUrlOptions imageOpts = new MediaUrlOptions { Scale = scale };
		imageUrl = Sitecore.StringUtil.EnsurePrefix('/', Sitecore.Resources.Media.MediaManager.GetMediaUrl(imageItem, imageOpts));
	}
	else
	{
		Log.Warn("Could not retrieve image item in GetImageUrl.", typeof(Sandbox.SitecoreExtensions));
	}

	return imageUrl;
}

As you can see, we’re passing in the Sitecore item, the name of the image field within that Sitecore item as a string, and the desired scale as a floating point number. The scale for the retina version of the image being 1.0, or 100 per cent, and the scale for our standard image being .5, or 50 per cent. After verifying that the image, and associated media item aren’t null, we create a new object of type MediaUrlOptions and set the Scale property to our desired scale. From there, we can call the Sitecore.Resources.Media.MediaManager.GetMediaUrl method, passing in the media item, and our MediaUrlOptions object, which will return our image url.

Now, let’s take a look at how we mark up our image using the srcset attribute, and our Sitecore extension method. The new srcset attribute enables you to specify a list of image sources to use based on the width of the browser and the pixel density of the device. In our solution, we are only using the pixel density to determine whether the standard image should be chosen, or whether the retina image should be chosen. On regular resolution displays, the 1x variant of srcset will be used, and on displays with 2 device pixels per CSS pixel, the 2x, or retina, variant of the srcset will be used. This puts the work of determining which image to use, on the browser.

Logo

We are passing the 50 per cent scaled image url into the src attribute to handle JavaScript disabled legacy browsers. We are again passing that 50 per cent scaled image url into the srcset attribute as the 1x variant, and our unscaled, retina image url in as the 2x variant using our GetImageUrl Sitecore extension method.

This nearly solves our problem, but because srcset isn’t supported in all browsers, we needed to use the respimage responsive image polyfill to expand support to a wider range of browsers and devices. The respimage script only needs to be added to your website, or bundled in your normal JS and will automatically run, and polyfill all images.

Conclusion

By leveraging Sitecore image scaling, the new srcset attribute, and the respimage responsive polyfill, we were able to implement a basic, and author friendly retina image approach for Sitecore. This approach could be expanded upon to utilize the srcset width description, and even the image sizes attribute if needed.

Notes

In Sitecore versions 7.5 and greater, a new security feature will prevent the GetImageUrl extension method from returning the same result. Sitecore now requires a generated hash value to be appended to the querystring. You will need to use Sitecore’s hashing utilities to append the hash value to the media URL using the Sitecore.Resources.Media.HashingUtils utility methods. I’ve provided an example of the updated extension method below.

public static string GetImageUrl(this Item item, string fieldName, float scale)
{
	Assert.ArgumentNotNull(item, "item");
	Assert.ArgumentNotNull(fieldName, "fieldName");

	string imageUrl = string.Empty;
	ImageField image = item.Fields[fieldName];

	if (image != null && image.MediaItem != null)
	{
		MediaItem imageItem = new MediaItem(image.MediaItem);
		MediaUrlOptions imageOpts = new MediaUrlOptions { Scale = scale };
		string mediaUrl = Sitecore.StringUtil.EnsurePrefix('/', Sitecore.Resources.Media.MediaManager.GetMediaUrl(imageItem, imageOpts));
		if (!string.IsNullOrWhitespace(mediaUrl)) {
			imageUrl = Sitecore.Resources.Media.HashingUtils.ProtectAssetUrl(mediaUrl);
		}
	}
	else
	{
		Log.Warn("Could not retrieve image item in GetImageUrl.", typeof(Sandbox.SitecoreExtensions));
	}

	return imageUrl;
}

Share