Lazy Loading Images in Orchard

One thing I've been meaning to do forever is implement lazy loading for images. After all, if an image is below the fold, and the user never scrolls down the page, why waste bits downloading it?

While I was at it I also implemented a simple fade-in animation. 

Requirements

This article assumes you're using Orchard 1.9 with the hiphouse swatch of the Cascade.Bootstrap theme, but the principles are simple and you can adapt it to pretty much any web page.

How It Works

Lazy loading is accomplished by a small amount of Javascript (shown below). As soon as document ready fires all images with a class of reveal are stipped of their src attribute, preventing them from displaying. The src url is saved to an attribute called data-src so the image can be loaded correctly at the appropriate time.

The second phase is triggered by a load, resize or scroll event. In response to one of these events, including the initial load event, all images that have not yet been revealed and have a data-src attribute are tested to see if any part of the image is visible in the browser viewport. An image that is visible has a load event handler called fadeInImage added to it that fades in the image by swapping the reveal class for a reveal-fade-in class. The reveal class simply hides the image by making it transparent, and reveal-fade-in animates an image fade-in.

How to Set an Image to be Lazy Loaded

So how do you set up an image so that it is lazy-loaded? Simply add the class reveal to it.

Javascript

Since jQuery is loaded for other reasons, the code takes advantage of it. 

Here's the JS you'll need to include. If you're using the hiphouse swatch from Cascade.Bootstrap then it's already included in the custom.js file.

$(document).ready(function () {

	// Immediately remove the src attribute from all images to be 'revealed'
	// if an image has already started loading it will be cancelled
	$('img.reveal').each(function () {
		var t = $(this);
		t.attr('data-src', t.attr('src'));
		t.removeAttr('src');
	});

	var fadeInImage = function (e) {
		e.stopPropagation();
		e.data.removeClass('reveal').addClass('reveal-fade-in');
	}

	// load and reveal images when they appear in the viewport
	var loadImage = function () {
		$('img[data-src].reveal').each(function () {
			if (isElementInViewport(this)) {
				var t = $(this);
				
				// fade-in the image after it has loaded
				t.one('load', t, fadeInImage);

				// load the image
				t.attr('src', t.attr('data-src')); 
				t.removeAttr('data-src'); // only process this image once
			}
		});
	}

	function isElementInViewport(el) {

		var rect = el.getBoundingClientRect();

		return (rect.right >= 0
			&& rect.bottom >= 0
			&& rect.left <= (window.innerWidth || document.documentElement.clientWidth)
			&& rect.top <= (window.innerHeight || document.documentElement.clientHeight));
	}

	$(window).on('load resize scroll', loadImage);

});

CSS

There are only two classes you need to support fade-in. These are already included in the hiphouse swatch in the Cascade.Bootstrap theme.

.reveal-fade-in {
    opacity: 1;
    -moz-transition: opacity linear 1s;
    -o-transition: opacity linear 1s;
    -webkit-transition: opacity linear 1s;
    transition: opacity linear 1s;
}

.reveal {
    opacity: 0;
}

 


1 Comment

Post Reply