eyes lou lucie-2 lucie

Today CSS animation can be used to progressively enhance websites. The code for my responsive image slider is a popular way of achieving this, but has three potential impediments for developers:

  • Some find the math for creating keyframes a little daunting, especially when the slider shows more images than the four of the original example.
  • Developers don’t always remember to add browser vendor prefixes.
  • Duplicating the first image to the end of the filmstrip to create a seamless transition is sometimes neglected, leaving a broken animation.

There’s a very good argument for automating these actions with JavaScript. The result will animate using CSS in all modern browsers, without having to worry about code, frameworks, plugins or prefixes.

There are just three conditions:

  • All images must be the exact same size.
  • The images must be placed within an element with an id of slidy
  • #slidy must be inside a container that has overflow: hidden applied to it.

The script also has the option of changing the pace of the animation by altering the values of two variables.

Step One

Place the images inside the #slidy element. For this example I’ll use a <figure>:

<div id="slidy-container">
	<figure id="slidy">
		<img src="eyes.jpg" alt>
		<img src="lou.jpg" alt>
		<img src="lucielle.jpg" alt>
		<img src="lucie.jpg" alt>
	</figure>
</div>

I’ve wrapped a <div> around the <figure> to serve as a container.

Step Two

The outer container can be given almost any appearance you wish, so long as it has overflow: hidden placed on it:

#slidy-container {
	width: 100%;
	overflow: hidden;
}

Step Three

Finally, add the JavaScript to the end of the page:

var timeOnSlide = 3,
timeBetweenSlides = 1,
animationstring = 'animation',
animation = false,
keyframeprefix = '',
domPrefixes = 'Webkit Moz O Khtml'.split(' '),
pfx  = '',
slidy = document.getElementById("slidy");
if (slidy.style.animationName !== undefined) {
	animation = true;
}
if ( animation === false ) {
	for ( var i = 0; i < domPrefixes.length; i++ ) {
		if ( slidy.style[ domPrefixes[i] + 'AnimationName' ] !== undefined ) {
			pfx = domPrefixes[ i ];
			animationstring = pfx + 'Animation';
			keyframeprefix = '-' + pfx.toLowerCase() + '-';
			animation = true;
			break;
			}
		}
	}
if ( animation === false ) {
  // animate using a JavaScript fallback, if you wish
	} else {
		var images = slidy.getElementsByTagName("img"),
		firstImg = images[0],
		imgWrap = firstImg.cloneNode(false);
		slidy.appendChild(imgWrap);
		var imgCount = images.length,
		totalTime = (timeOnSlide + timeBetweenSlides) * (imgCount - 1),
		slideRatio = (timeOnSlide / totalTime)*100,
		moveRatio = (timeBetweenSlides / totalTime)*100,
		basePercentage = 100/imgCount,
		position = 0,
		css = document.createElement("style");
		css.type = "text/css";
		css.innerHTML += "#slidy { text-align: left; margin: 0; font-size: 0; 		position: relative; width: " + (imgCount * 100) + "%;  }";
		css.innerHTML += "#slidy img { float: left; width: " + basePercentage + "%; }";
		css.innerHTML += "@"+keyframeprefix+"keyframes slidy {";
		for (i=0;i<(imgCount-1); i++) {
			position+= slideRatio;
			css.innerHTML += position+"% { left: -"+(i * 100)+"%; }";
			position += moveRatio;
			css.innerHTML += position+"% { left: -"+((i+1) * 100)+"%; }";
		}
	css.innerHTML += "}";
	css.innerHTML += "#slidy { left: 0%; "+keyframeprefix+"transform: translate3d(0,0,0); "+keyframeprefix+"animation: "+totalTime+"s slidy infinite; }";
	document.body.appendChild(css);
}

I’ve left a fully commented version of the script on CodePen, together with a GitHub repo. The code is responsive in more ways than one: remove an image from inside the #slidy container or add several, and the JavaScript will always create the correct CSS animation to match.

There are two user-defined variables at the very beginning of the script that you can adjust:

  • timeOnSlide is the amount of time, in seconds, for each image to remain static.
  • timeBetweenSlides is the time to move from any one slide to the next, also in seconds.

Both variables can take floating point values. For example, timeBetweenSlides = 1.5, will set a one-and-a-half second transition between each image.

Fallbacks

Graceful degradation in older browsers (mostly <IE10) or those blocking JavaScript could be added with a few more lines of CSS:

#slidy {
	margin: 0;
	font-size: 0;
	position: relative;
	width: 400%;
}
#slidy img { width: 25%; }

(This assumes that you have four original images).

The result will be a static, responsive image (the first in the series) without animation.

Photographs by AP Photographie and Konstantin DaCosta, licensed under Creative Commons.

Enjoy this piece? I invite you to follow me at twitter.com/dudleystorey to learn more.
Check out the CodePen demo for this article at https://codepen.io/dudleystorey/pen/xDrCw