With new features in CSS 3 such as animation and three-dimensional transforms, you can do some pretty amazing things. With multiple animations on one element, it’s possible to take a sprite sheet and animate it frame by frame. This tutorial assumes basic knowledge of CSS3. Knowing CSS3 animations is a plus.
At the time of this tutorial, only Chrome, Safari, and possibly Internet Explorer are supported. This is due to inconsistent support of Update 16 Jan 2018: Almost all the major browsers now support background-position-x
and background-position-y
among different rendering engines. If you find that your animations do not work, try using one of these browsers.background-position-x/y
consistently, meaning that this tutorial should work in Chrome, Firefox, Safari, IE, Microsoft Edge, and Opera. You may check https://caniuse.com/#search=background-position for the latest cross-browser compatibility.
What’s a sprite sheet?
A sprite sheet is a single image that consists of a bunch of smaller images. The easiest way to explain it is to look at a picture of one:
Reading from left to right, top to bottom, notice how our figure changes position ever so slightly. In fact, if we cut this large image up into its constituent images and rapidly flash them in sequence, we will get the illusion of movement.
Primer: one-dimensional sprite sheet animation
Before delving into two dimensions, let’s take a look at how we can animate just the top row of the sprite sheet above.
First we must know how to show one part of the image. The easiest way to achieve this is with a div
of set width and height, and a background image. There are other ways of achieving this, but we will stick with this method.
<div id="sprite"></div>
#sprite { height: 294px; width: 165px; background-image: url(http://www.fabiobiondi.com/blog/wp-content/uploads/2012/08/runningGrant.png); background-position: 0 0; }
Setting the width and height on the div effectively crops the background image. The specific numbers are derived from the dimensions of each frame of the sprite sheet. The important property here is background-position
, which we will use to shift the background image inside the div
. Right now it is set to 0 0
, which shows the background image starting at the top-left corner of the image.
Defining the animation
What we want to do then, is rapidly change the value of background-position
in our animation. First, let’s define that animation. Add this to the bottom of your CSS.
@keyframes run-x { from { background-position-x: 0; } to { background-position-x: -1980px; } }
In the code above, we start at 0
for our background-x-position
. By the end of the animation, the background will have shifted to -1980px
. How did we arrive at this number? Let’s temporarily ignore the negative sign. Examining the image of running Grant, we see that each “frame” is 165 pixels wide. The sprite sheet is 12 frames across. By simple math, we figure out that the last frame is positioned at 12 Ć 165px = 1980px.
Now you may wonder why there is a negative sign. Background position is a strange beast; it is not specified in terms of the image itself, but rather the element that has the background image. Do not worry too much about whether to use a negative sign, as this tutorial will give you the exact numbers to use.
Apply the animation
Right now nothing on your screen will move. That’s because we haven’t referred to the animation yet. Now we’ll modify the CSS for #sprite
. The changes are highlighted.
#sprite { width: 300px; width: 165px; background-image: url(http://www.fabiobiondi.com/blog/wp-content/uploads/2012/08/runningGrant.png); background-position: 0 0; animation-name: run-x; animation-duration: 0.4s; animation-iteration-count: infinite; animation-timing-function: steps(12); }
Note: when using Chrome or Safari, prefix all animation-*
properties with -webkit-
. For example, animation-name
becomes -webkit-animation-name
.
The four lines of code we added define the animation that is applied to the element and some properties of the animation.
animation-name
is name of the animation is what we just defined: “run-x”.animation-duration
says that this animation lasts for 0.4 secondsanimation-iteration-count
sets the animation to loop ad infinitum- The
animation-timing-function
,steps(12)
tells us to break up the animation into twelve discrete stages. Try removing this line of code. You’ll notice a sort of filmstrip effect, where it feels like someone is pulling the sprite sheet at a rapid speed. This is because the animation is hitting every value between 0 and -1980 pixels. Thesteps
function makes it so that the background is at0
for some time, then-165px
, then-330px
, and so on. Rather than a continuous progression of values,steps()
makes the CSS properties take on discrete values per unit of time. And this is really what sprite animation is all about: taking a frame and displaying it for a short time, showing the next frame and displaying that frame for a short time, and so on.
Intermediate step: another animation
If we want the animation to run in two dimensions, then we have to change the y-position of the background along with the x-position. The concept is the same as before; only this time we will use background-position-y
instead of background-position-x
. At the bottom of your CSS, add a new keyframe definition.
@keyframes run-y { from { background-position-y: 0; } to { background-position-y: -1764px; } }
Each frame’s height is about 294 pixels, and the sheet is 6 frames tall. 294 \times 6 = 1764px. Note that we don’t have to touch background-position-x
. We already have an animation definition for manipulating this property.
We will not apply this animation immediately. Instead, we’ll combine it with the run-x
animation we defined above to produce the 2D animation.
Putting it all together
One of the beautiful things about CSS3 animations is that it can accept multiple animations per element. We have already applied the animation in the x-direction. Now, we just add on the y-direction animation.
#sprite { width: 300px; width: 165px; background-image: url(http://www.fabiobiondi.com/blog/wp-content/uploads/2012/08/runningGrant.png); background-position: 0 0; animation-name: run-x, run-y; animation-duration: 0.4s, 2.4s; animation-iteration-count: infinite; animation-timing-function: steps(12), steps(6); }
And there you have it! Here’s an explanation of the changes made in order to produce the effect:
- I added
run-y
toanimation-name
to apply another animation at the same time as the existingrun-x
animation. - Skipping to the timing function,
run-y
is presented in six discrete steps, as the sprite sheet is 6 frames tall. This will become important in determining the duration ofrun-y
run-x
runs in 0.4 seconds, andrun-y
runs in 2.4 seconds. How did we arrive at these numbers? The idea is that we want to run the first row completely, then immediately jump to the next row and run its length, and so on. It takes 0.4 seconds to run an entire row of the sprite sheet. Think of therun-y
animation as “selecting” a row. We want to select a row for 0.4 seconds, then select the next row for 0.4 seconds, and so on. There are six rows altogether in the sprite sheet, sorun-y
should take 0.4 Ć 6 = 2.4 seconds to run.- Both animations run on loop, so
animation-iteration-count
is not changed.
A short word about shorthand
Instead of writing
animation-name: run-x, run-y; animation-duration: 0.4s, 2.4s; animation-iteration-count: infinite; animation-timing-function: steps(12), steps(6);
we can write
animation: run-x 0.4s infinite steps(12), run-y 2.4s infinite steps(6);
This is called shorthand, where multiple CSS properties are compressed into one. The animations themselves are separated by commas, while animation properties are separated by spaces.
The whole thing
You can see a complete demonstration, along with source code, at http://codepen.io/g-liu/pen/XbrMzr?editors=110. At the time of this post, the demo only works on Chrome and Safari, due to lack of support for background-position-x
and background-position-y
.
Hi, can I use your sprite sheet in my own game?
This spritesheet is not my own creation, but borrowed from http://smwiki2014.wikidot.com/wiki:android-game-development, which is licensed under Creative Commons Attribution-ShareAlike 3.0 License. As long as you follow the terms of that license (https://creativecommons.org/licenses/by-sa/3.0/), then it’s fine.
“Good point. This is actually caused by the sprite sheet itself. In the last row there are some blank frames and they show up in the animation.”
And how to fix this error? Is there a code for this?
The sprite sheet is just an example, this CSS animation technique will work with any sprite sheet. You’ll need to find one that has no blank frames. Or, you can take the current sprite sheet and reformat it so there are no blank frames. I don’t believe CSS itself is powerful enough to know to “skip the last N frames”.
Hi, great tutorial. Will this be responsive?. Can you please explain me with code, if this won’t be an responsive css sprite animation.
If you want the animation to be as large as the user’s screen, you should play around with the
background-size
property.If each frame of your animation is very large, and you want it to fit on small screens, you can also take a look at the
max-width
andmax-height
properties.this Animation not work in Firefox browser . How to solve this ?
Hello nandansahoo!
This is because
background-position-x
andbackground-position-y
are not recognized by Firefox (see 2nd paragraph of the post.) You can solve this problem with a polyfill, like this one.thanks for the explanation. It help me to learn.
theres no idle animation š
I like the jumping guy… must be missing a keyframe because he disappears at the end of his hop.
Good point. This is actually caused by the sprite sheet itself. In the last row there are some blank frames and they show up in the animation.