Images animating into view whilst scrolling the browser window

July 9, 2014

In a recent project I was tasked with adding an animation to images whilst the window was scrolling.

The basic gist was, the images should animate in to their end-location when the user scrolls down and the images are in view. Once the image is in it’s end-location it should stay there until the user scrolls back up. When the user scrolls down again, the animation should be repeated.

I decided that I wanted to do the animating part with CSS3, mostly because of speed, this decision meant that older browser would not have an animation, but that’s a small price to pay seeing as this is only some extra eye-candy, and I felt that it would’ve been more expensive animating with either padding/margin or absolute positioning.

What I needed was a Javascript class that checks whether a specific element is in view (or if it’s been in the view i.e. the user has scrolled the browser past the element) the class should also check if the element is outside of the view (or if the user hasn’t yet seen the item i.e. the item is below the browser window).

Once the element is in the browser view, or if it’s been in the view, it should get the CSS class ‘active’. Once it is outside of the view, the class should be removed. Easy enough. Basically what you need to know is, the window height, the window scroll position, the element’s offset to the window and the element height. When you have these parameters you can calculate the Y-position of the element’s top and bottom, and you can calculate the top and bottom of the window, and therefore check if the element is inside of it.

/**
 * Checks if an element is within view of the browser window.
 *
 * @param {element} element The element to check.
 *
 * @return {bool}
 */
this.isItemInView = function(element) {
	var windowHeight = $(window).height();
	var windowTop = $(window).scrollTop();
	var windowBottom = windowTop + windowHeight;

	var elementHeight = $(element).height();
	var elementTop = $(element).offset().top;
	var elementBottom = elementTop + elementHeight;

	return (windowBottom >= elementTop && windowTop <= elementBottom);
};

This is the class that I ended up writing, ImageScroll.js. The class accepts one parameter, a selector, for instance ‘.scrollable-image’.

Here’s a typical initialization script. When the document has loaded, the bootstrap class will fire up the image scroller class, checking for any element that has the data attribute ‘data-bh-scroll-image’.

;(function(App, window, document, undefined) {
	'use strict';

	/**
	 * Starts the application
	 */
	App.Bootstrap = function() {
		/**
		 * Starts the application
		 *
		 * @return {void}
		 */
		this.initialize = function() {
			new App.ImageScroll('[data-bh-scroll-image]');
		};

		this.initialize();
	};

	// On document ready, fire bootstrap.
	$(document).ready(function(){
		new App.Bootstrap();
	});

}(window.BH = window.BH || {}, window, document));

At this point we know when the element is in view, or has been in view, so now all we have to do is animate the element.

This is how the element looks in the markup:

<!-- The container that has the attribute, this one will get the class 'active' -->
<div class="image-one" data-bh-scroll-image="">
	<!-- The image -->
	<img src="images/can_sides.jpg" />
</div>

As you can see I’m actually not check if the image is within view, I’m checking if the image’s parent element is within the view. This is because we’re going to animate (or move) the image, while the parent will stay put. If we view-detect the image, the animation offset will become problematic when we try to detect it’s offsets.

Now for the animation. As I already said, the detection happens on the parent element, not the image, this means that parent will get the ‘active’-state, not the child image. So we need to act accordingly in the CSS.

/* Target the child image, not the element with the attribute */
[data-bh-scroll-image] > img {
	display: block;
	/* Add transition values */
	transition: all 300ms linear;

	/* Set the default values that we're going to animate */
	opacity: 0;
    transform: translate(0px, 100%);
}

/* Once the target-element gets it's active state, change the position
 * of the image */
[data-bh-scroll-image].active > img {
	/* Set end values for the animated properties */
	opacity: 1;
    transform: translate(0px, 0px);
}

And now, whenever the parent-element gets the CSS class ‘active’ the image will be animated to the correct place.

As usual, I’m providing you with an example, and all of the code. Try scrolling up and down in the example and you’ll see how everything works. Please also notice that this isn’t a fully-tested script, I’ve written it as a test and should therefore not be used as-is (though you can if you want), you should see it more as an example on how you could solve this kind of problem.

Tags