demosthenes.info

I’m Dudley Storey, the author of Pro CSS3 Animation. This is my blog, where I talk about web design and development with , and . To receive more information, including news, updates, and tips, you should follow me on Twitter or add me on Google+.

web developer guide

my books

Book cover of Pro CSS3 AnimationPro CSS3 Animation, Apress, 2013

my projects

CSSslidy: an auto-generated #RWD image slider. 1.5K of JS, no JQuery. Drop in images, add a line of CSS. Done.

tipster.ioAutomatically provides local tipping customs and percentages for services anywhere.

Square-tailed kite
Square-tailed kiteWhite-tailed kiteHawkOspreySquare-tailed kite

Make A CSS3 Animated Image Slider

css / galleries

Estimated reading time: 5 minutes, 21 seconds

Slider galleries have been around a long time, but until very recently they have almost exclusively been coded using . CSS3 now gives us the ability to create the same effect natively in the browser, without JavaScript or any dependence on frameworks or plugins.

For the purposes of illustration I’ll keep the animation sequence fairly basic; as and variables start to be supported cross-browser, you’ll find that you will be able to add a great many more effects to this simple code.

The principles of the slider are very simple: the elements on the page consist solely of a container (an HTML5 <header> element, in this case) and a “strip” of slider gallery images side-by-side. The outer container element will act as a window through which we will view each image in the strip sequence.

The Markup

<header>
<div>
<img src="square-tailed-kite.jpg" alt="Square-tailed kite">
<img src="white-tailed-kite.jpg" alt="White-tailed kite">
<img src="hawk.jpg" alt="Hawk">
<img src="osprey.jpg" alt="Osprey">
</div>
</header>

There are just two conditions for the elements:

  1. All the images must be exactly the same size (the photographs I am using in the example have been consistently sized to 800px x 341px with PhotoShop).
  2. The outer container element must be the exact same size as one of the images.

Base CSS

header { border: 1px solid black; background: #000 }
header, div img { width: 800px; height: 341px; }

Viewing the page in a browser, you’ll notice that the images spill outside the <header> element, and that they don’t stretch out in a single horizontal line. Let’s fix that by hiding any content outside the header element:

header { overflow: hidden; }

And make the div the width of all the images added together (800 × 4 = 3200 pixels).

div { width:3200px;  }

Showing the next image is simple: we just have to move the div the width of one image (800px). You can prove this by altering the CSS momentarily to include the following. (Note that I've dropped vendor prefixes for this example).

div { transform: translateX(-800px); }

The next image in the sequence will be shown, with the images before and after hidden due to overflow: hidden on the header.

Crafting The Animation

As I’ve previously discussed, initializing a CSS3 keyframed animation is very much like using an embedded font: we name and specify the animation at the top of our stylesheet, then call it later in our code.

Animation steps are measured in percentages between the start (0%) and end (100%) frames. The start is easy: we want the slider to begin exactly where it is. We’ll name the animation slider and set that first position:

@keyframes slider {
0% { transform: translateX(0px); }
}

We need to divide the time for the animation between two actions: displaying an image, and moving to the next one. I’ll make the arbitrary decision to hold each image for 20% of the animation (meaning the image display will take a total of 80% of the total time of the animation). We can now add the second keyframe:

@keyframes slider {
0% { transform: translateX(0px); }
20% { transform: translateX(0px); }
}

Because the information remains the same between the first and second keyframe, the div doesn’t change. If the total animation was 10 seconds long, the first image would be held for 2 seconds.

We need to move the div 800px in 5% of the total animation time, and then hold for another two seconds:

@keyframes slider {
0% { transform: translateX(0px); }
20% { transform: translateX(0px); }
25% { transform: translateX(800px); }
45% { transform: translateX(800px); }
}

Completing our animation to cover every image is easy: we’re just using multiples of 800px for the movement and intervals of 20% and 5% for the animation:

@keyframes slider {
0% { transform: translateX(0px); }
20% { transform: translateX(0px); }
25% { transform: translateX(800px); }
45% { transform: translateX(800px); }
50% { transform: translateX(1600px); }
70% { transform: translateX(1600px); }
75% { transform: translateX(2400px); }
95% { transform: translateX(2400px); }
100% { transform: translateX(0px);  }
}

We call the animation from the div itself:

div {
animation-name:slider;
animation-duration:10s;
animation-timing-function: ease-in-out;
animation-iteration-count:infinite;
}

There’s an issue with the animation we’ll discuss in a moment, but right now we’re going to appreciate the fact that the time for the animation sequence is held separately from the keyframes: if you feel the animation is too fast, just modify the value of animation-duration.

Now the issue. As you can see, the div moves past the last image, then whips back to the starting position.

Getting Back To The Start

has the ability to append elements on the fly: that’s the reason that JavaScript slider animations give the impression of being an endless loop. Such capabilities are beyond the ability of CSS at the moment, so instead we’ll hide the return to the start by adding a copy of the first image at the end.

<header>
<div>
<img src="square-tailed-kite.jpg" alt="Square-tailed kite">
<img src="white-tailed-kite.jpg" alt="White-tailed kite">
<img src="hawk.jpg" alt="Hawk">
<img src="osprey.jpg" alt="Osprey">
<img src="square-tailed-kite.jpg" alt="Square-tailed kite">
</div>
</header>

You'll need to make the inner div wider to accomodate the added image:

div { width:4000px;  }

And move the animation to the last image, so that the transition to its exact copy in the first image is invisible:

95% { transform: translateX(2400px); }
100% { transform: translateX(3200px);  }

The first and last frames now match, with no motion visible between them, making for a seamless animation.

Pausing The Animation

It’s reasonable – even expected – that the user should be able to pause the animation in order to focus on one image. We can do that with a hover pseudo-selector and animation-play-state:

div:hover { animation-play-state:paused; }

That pauses the animation, but it would be nice to have some visual indication that it is stopped. We’ll do so with a piece of generated content, triggered from hovering over the <header> element (since the <div> is always moving, it’s a lot easier to position the pause button relative to the <header>). We’ll use two glyphs of the Unicode symbol U+275A, which kind of looks like a pause symbol:

header:after { content: "❚❚"; font-size: 150px; position: absolute; z-index: 12; color: rgba(255,255,255, 0); left: 300px; top: 80px; transition: 1s all ease-in-out; }

The pause symbol is invisible by default: we’ll make it visible with a sequence of chained pseudo-selectors:

header:hover:after { color: rgba(255,255,255, 0.6); }

The Future

It would be reasonable to assume that each image would link through to a larger version of itself, or other content. It should be noted that the “slider” div could contain any content at all: not just images, but text, videos, iframes, anything. I've also created an enhanced responsive version of the slider.

The ideas presented here will be taken much further as CSS3 is supported in more browsers. Caleb Jacob deserves kudos for spearheading the concept. Play with this code on CodePen

comments powered by Disqus

This site helps millions of visitors while remaining ad-free. For less than the price of a cup of coffee, you can help pay for bandwidth and server costs while encouraging further articles.