My recent optimized particle clock made use of a sprite sheet that was created by prerendering to an off-screen canvas. I thought it might be helpful for beginners to see another simple example of this technique, this time to create an evolving animation.

Click here or on the screenshot below to see it in action, and then read my comments below to see how the sprite sheet is created. (A mobile version with a far fewer dots can be viewed here.)

sprite sheet screencap

Download

Download the full commented source code here: SpriteSheet.zip

About the code

A sprite sheet can be used whenever you want an animation which has a reasonable number of predictable animation frames. For simplicity, I have animated some evolving dots here, but the same idea could be applied to other graphics, especially those used for games. In this example, the dots grow from zero radius to a maximum radius of 60 (and then back down to 0 again), and that’s all they do, so there really only needs to be about 60 different images which form the animation (we could probably even get away with fewer).

The animation consists of dots which grow in size and change their color. At run time, we draw the individual animation frames of the dots to a sprite sheet, which in this case is actually an off-screen canvas created within the code. Then later when we wish to animate many dots, we just repeatedly copy from this sprite sheet rather than drawing the dots again with graphics commands.

The maximum size of each dot is 60 X 60, therefore the sprite sheet is made to be 3600 X 60 so that it can contain 60 different 60 X 60 images lined up in sequence. If we were really clever, we could save memory and increase drawing speed by creating the sprite sheet so that each frame of the animation is contained in a rectangle only as wide as necessary. However, this would raise issues later on as to how we determine the location of a particular frame in the animation. Making them all the same size makes this simple to do.

Creating the sprite sheet is done with the createElement() method:

spriteSheetCanvas = document.createElement('canvas');
spriteSheetCanvas.width = dotDiam*numStages;
spriteSheetCanvas.height = dotDiam;
spriteSheetContext = spriteSheetCanvas.getContext("2d");

Note that dotDiam represents the maximum dot diameter, and numStages is the number of frames that will be drawn, thus the width is set to the product of these numbers in order to contain all of the frames arranged in a horizontal sequence. After creating the sprite sheet, we draw the animation frames into it.

Later when we want to animate the dots, we grab the relevant frame from the sprite sheet and copy it to the displayed canvas:

context.drawImage(spriteSheetCanvas,
                  Math.round(d.stage)*dotDiam,0,dotDiam,dotDiam,
                  d.x - dotRad,d.y - dotRad,dotDiam,dotDiam);

Here d represents one of the dots, represented by an abstract Object with parameters for position, current stage (frame), and change rate. The nine inputs in the drawImage() method set the spriteSheetCanvas as the source, the position and size of the rectangle within the source from which to copy, and finally the position and size of the rectangle into which to draw the source pixels. The stage parameter is not necessarily set to a whole number (in order to allow variable animation rates among the dots), so the Math.round() function is applied to find the closest appropriate animation frame.