Day 11 – Animating with sprite sheets

The holidays are over and we’re all back at the office getting back to the regular routine and feeling a bit like robots. No? OK, that’s maybe a stretch. Hopefully you at least like robots though, because we’re about to animate a little robot heart using sprite sheets twice: one using canvas and one using a div.

Animating with a sprite sheet is like moving a connected series of images past a set viewing area. Think of a viewmaster or a film strip if you’re old enough.

The image below shows you approximately what’s going on behind the scenes. We have one big image that contains every frame of the robot animation (the sprite sheet) and we display one at a time, moving along and down our list of images as we progress through the animation. Do this fast enough and you have animation :)

The sprite sheet

We’ll be using the same sprite sheet image (this one) for both variations. I won’t get into how to create a sprite sheet here, but there are a number of ways to make them and you certainly don’t have to put them together all by hand. I’ve used Keith Peter’s SWFSheet to export the sprite sheet I’m using here from a SWF.

First up, the canvas version

Once we’ve done our initial file set up, the first thing to do is load in our sprite sheet image. When we know our image has loaded, we set an interval to call our loop function 30 times a second, which is where everything happens.

	//load the image
	image = new Image();
	image.src = "robot2.png";
 
	image.onload = function() {
            //we're ready for the loop
            setInterval(loop, 1000 / 30);
        }

Great! we’re ready to actually make something happen. The loop function starts out easily enough with a call to clearRect(…) to clear our canvas. But what’s with that excessively long drawImage(…) function? It’s a bit ugly.

c.drawImage(image, xpos, ypos, frameSize, frameSize, 0, 0, frameSize, frameSize);

All those extra arguments are there because we are defining two different rectangles The first rectangle (xpos, ypos, frameSize, frameSize) defines the rectangle in our source image that we want to copy from. This will change every frame because we are updating the xpos and ypos values each time. The second rectangle (0, 0, frameSize, frameSize) defines where in our canvas we are drawing to. For this example that will never change. (You might also notice that both my width and height are being set to frameSize. To keep things simple, I’m using a square image area.)

Next up, we do a little bit of math to figure out exactly which part of our sprite sheet we should draw from to display the correct frame:

//each time around we add the frame size to our xpos, moving along the source image
xpos += frameSize;
//increase the index so we know which frame of our animation we are currently on
index += 1;
 
//if our index is higher than our total number of frames, we're at the end and better start over
if (index >= numFrames) {
	xpos =0;
	ypos =0;
	index=0;	
//if we've gotten to the limit of our source image's width, we need to move down one row of frames				
} else if (xpos + frameSize > image.width){
	xpos =0;
	ypos += frameSize;
}

We add our frameSize to our xpos to move along our sprite sheet horizontally (remember, our images our square). We also increment an index counter to keep track of what frame we’re on. If our index is higher than the total number of frames we have, it’s time to start over. If our next horizontal move would have us going past the width of our sprite sheet, we move down to the next row. This repeats over and over, creating our animation. Play with it here on JSFiddle.

One down!

Once more, with divs

I think you’re getting the hang of it now, so I’ll do a little less explaining for this one. We’re going to do pretty much the exact same thing except this time we’re calculating how to move the background image of a div so that the correct frame in the animation is visible.

The first notable difference is in our small block of CSS:

#anim {
	width:200px;
	height:200px;
	background-image: url(robot2.png);
}

We’re identifying our target div anim, setting its height and width (our viewable area), and assigning its background image (our sprite sheet).

Our loop function looks incredibly similar, or you might even say nicer since it doesn’t have nearly as many lines of comments. The only real difference is the first line:

div.style.backgroundPosition = (-xpos)+"px "+(-ypos)+"px";

We’re repositioning #anim‘s background image every frame. The calculations to determine the xpos and ypos values are the same as in the canvas version so the rest of the function should look very familiar. But you can see we’re using -xpos and -ypos – this is because we want to move the background image up and to the left to reveal the next frame.

So there you have it, the same sprite sheet animation two ways! Hopefully it was even a bit fun :)

And with that, we wrap up Day 11 of the 12 days of CreativeJS. Only one more to go!

18 thoughts on “Day 11 – Animating with sprite sheets

  1. We looked into converting an existing MovieClip timeline based Flash game into something for JS running on prerendered spritesheet’s (we use a runtime MovieClip rasterizer in Flash so it was easy to generate the sheets from our existing assets).

    Turned out the 2 MB of source vector animations exploded in hundreds of MB’s on PNG sheets.

    • It’s true that there’s nothing quite like an optimised super lightweight vector animation format like swf. And I see it as a missing need across a variety of platforms, not just JavaScript. There are more complex and economical ways to bring your Flash content into JavaScript, for example a system to convert each component part of your movieclip into bitmaps and export the transformation data along with it. But yes it gets complicated! Let’s hope we get better tools soon.

  2. It’s worth noting that the upper bound on image sizes on iOS is 1024×1024 (2048×2048 on iOS 5+). Any larger than that and the images will be displayed at a lower resolution (as if upscaled from a lower resolution bitmap), so this should be considered when generating sprite sheets.

  3. Day 11 is a nice day for robot lovers.

    I hesitate a lot between canvas and divs for my current game project. DOM Elements may be a better choice for performance (as drawImage is quite slow) and multiple sprite layers compositing with relative positionning (such as a gun in your robot’s hand)

    Here is my implementation with my.js class system, work in progress : http://pastebin.com/kVkFh58F

    I was wondering if pure CSS animations were a better choice than modifying the style attribute directly in JavaScript. Sprite animations declaration will probably be much easier, but we will have less control on animation speed, pauses or frames skip.

  4. Cool stuff…

    2 questions:
    1. I still can’t understand the difference between the two methods mentioned
    2. I used div animation on my site. On regular browsers every thing is cool. On ios (iPad 3 – iOS 5) it doesn’t want to show up at all… Any clue what’s wrong?
    Example: pr-web-services . com

  5. Thank you very much indeed. the tutotial was very usefull and for someone who wants to see the difference between canvasd and the old div way, it was very lightening.
    I’m a game developer (browser based) and i waned to create a map, to show the peaoples coordination (with an image or circle), their information on the map and maybe some army movemenets.
    First I tried polymap, It did work but the performance is awfull, It nearly dead in many browser when i zoom out and the number of planets get high.
    So i tried to make a library from the base, with pan and zoom and it should be multi-layerd. Do you think It’s a good choice to create a lib from the scrach and also using the canvas in it?
    What would be my challenges and pitfalls?

    • You could change how frequently the loop is called. In this line: “setInterval(loop, 1000 / 30);” you’re setting an interval to be called 30 times a second. If you replaced 30 with say 15, you’d be calling the loop half as often and the animation would run slower.

  6. I haven’t done any tests, but I would guess if either had a performance advantage it would be the < canvas > version. Browser support is pretty equal in modern browsers. Older browsers, like IE8 or lower, don’t have support for < canvas > so only the div version would work there. But hopefully you don’t have to worry about IE8 for whatever you’re working on :)

  7. Would be nicer to put all the code and working code in the tutorial,
    The jsfiddle was nicely commented but going by the post there is quite a few errors…

    Like “else if (xpos + frameSize > width){
    xpos =0;
    ypos += frameSize;
    }”
    width is supposed to be image.width

    But nice posting all in all.

    • Hi Greg,

      Thanks for catching that! I’ve updated the post to correct the differences.

      The jsfiddle is definitely the better place to play with the code directly since you can see everything at once :)

  8. Can’t thank you enough for this wonderfully clear tutorial! I had been trying to get Spritely to work for the past 3 hours, having no success; your javascript solution works perfectly, and what’s more, I understand what I’m doing so that I can modify it. Currently using the div version, but I plan to play around with it in canvas, too. Thank you very much!

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Current day month ye@r *