Skip to main content

Simple sprite animation using CSS3 @keyframes and steps()

This is something I did recently that I thought was pretty neat. The basic idea was to animate a graphic when it is hovered over. I initially thought I would have to do something crazy with javascript or possibly gifs to get it to work but in the end I came up with a way to do it using CSS3 @keyframe animation which is much cooler.

The concept is actually pretty simple - create a sprite map containing each frame of the animation and then use a custom @keyframe animation to cycle through the sprites in the form of a background image.

Create a sprite

So my animation is going to be 12 frames long and occur on a canvas size of 200x200px. You can stack the frames horizontally or vertically, it doesn't matter, but your sprite map needs to be exactly the right size (there should be no gap in between the frames). Here's mine, it's Morris Day, kindly animated by Luke Huitson (who also designed this very website!)


We also need something to display our animation in - just an empty div or something:

<div class="morris-day"></div>

Create the @keyframe animation

I am going to use the Animation Compass plugin which will generate all of the vendor prefixed versions of my animations. Make sure to include it and compass at the top of you scss file:

@import "compass";
@import "animation";

So on to the @keyframe animation itself. Again it's pretty simple, it animates the background-position:

@include keyframes(morris-animate) {
  from {background-position: 0 0;}
  to {background-position: -2400px 0;} // The width of the sprite.
}

Apply the animation!

First off, let's just put the first frame in our div:

.morris-day {
  width: 200px;
  height: 200px;
  background-image: url('morris-day.png');
}

Now we can actually animate it! The key here, the real magic that makes this work, comes from the steps() timing-function which breaks the animation up in to steps - in this case showing each frame separately, one after the other, without actually animating between them. The steps timing-function needs to know the number of steps it should take - in this case 12:

.morris-day:hover {
  @include animation(morris-animate .8s steps(12) forwards);
}

It works! Look at him go!

 

Browser support

So obviously not all browsers are going to support this so let's take care of that. I have already mentioned in a previous blog post the joys of Modernizr so I'm gonna make use of that here. The last frame of the animation is fine for a non-animated hover state so I'll use the classes provided my modernizr to provide that as the fallback.

.morris-day:hover {
  @include animation(morris-animate .8s steps(12) forwards);
  .no-cssanimations & {
    background-position: -2200px 0;
  }
}

That's it! Pretty simple concept really but I'm keen to hear if anyone else uses it!