var sprites = new SpriteSheet({
width: 32,
height: 32,
sprites: [
{ name: 'stand' },
{ name: 'walk_1', x: 0, y: 1 },
{ name: 'walk_2', x: 0, y: 1 },
The definition is simple: First, we define the width and height of a sprite, and then we define a name and x/y offsets for each sprite in the sheet.
When we apply this with an image which has three sprites, each 32×32 in size, we can simply tell the spritesheet that we want the sprite called ‘walk_1’, and we get a nice object with the X and Y positions to draw it from the sheet. We’ll see this in action soon!
Now that we have the spritesheet stuff done, we can do the last in the line: The animation class.
The animation class takes a definition, similar to how the spritesheet took, and then handles various things based on that. For example, as each animation comprises of specific frames, it calculates how long a frame has been visible, and changes to the next frame when it has been visible long enough.
var Animation = function(data, sprites) {
this.load(data);
this._sprites = sprites;
Animation.prototype = {
_frames: [],
_frame: null,
_frameDuration: 0,
load: function(data) {
this._frames = data;
//Initialize the first frame
this._frameIndex = 0;
this._frameDuration = data[0].time;
animate: function(deltaTime) {
//Reduce time passed from the duration to show a frame
this._frameDuration -= deltaTime;
//When the display duration has passed
if(this._frameDuration <= 0) {
//Change to next frame, or the first if ran out of frames
this._frameIndex++;
if(this._frameIndex == this._frames.length) {
this._frameIndex = 0;
//Change duration to duration of new frame
this._frameDuration = this._frames[this._frameIndex].time;
getSprite: function() {
//Return the sprite for the current frame
return this._sprites.getOffset(this._frames[this._frameIndex].sprite);
}
|
var Animation = function(data, sprites) {
this.load(data);
this._sprites = sprites;
Animation.prototype = {
_frames: [],
_frame: null,
_frameDuration: 0,
load: function(data) {
this._frames = data;
//Initialize the first frame
this._frameIndex = 0;
this._frameDuration = data[0].time;
animate: function(deltaTime) {
//Reduce time passed from the duration to show a frame
this._frameDuration -= deltaTime;
//When the display duration has passed
if(this._frameDuration <= 0) {
//Change to next frame, or the first if ran out of frames
this._frameIndex++;
if(this._frameIndex == this._frames.length) {
this._frameIndex = 0;
//Change duration to duration of new frame
this._frameDuration = this._frames[this._frameIndex].time;
getSprite: function() {
//Return the sprite for the current frame
return this._sprites.getOffset(this._frames[this._frameIndex].sprite);
The comments should explain most of this class. The main point is that with this, we are done!
Now we only need to look at the animation definition format, and then we can combine these three into the most amazing animation machine the internet has ever seen… or something like that anyway.
var walk = new Animation([
{ sprite: 'walk_1', time: 0.2 },
{ sprite: 'stand', time: 0.2 },
{ sprite: 'walk_2', time: 0.2 },
{ sprite: 'stand', time: 0.2 }
], sprites);
|
var walk = new Animation([
{ sprite: 'walk_1', time: 0.2 },
{ sprite: 'stand', time: 0.2 },
{ sprite: 'walk_2', time: 0.2 },
{ sprite: 'stand', time: 0.2 }
], sprites);
Here we define a walk animation. The animation is defined by simply giving the object an array, which contains objects for each frame, defining the name of the sprite and the duration of the frame. The variable sprites in the end is the spritesheet we defined in the previous example.
Putting it all together
Now, let’s put all this together!
I have a spritesheet lying around from some old projects:
kunio.gif
. Let’s animate this.
You may be familiar with this character – He is Kunio from an old NES game known as Downtown Nekketsu Monogatari, released in english as
River City Ransom
, and he’s been featured on various flash-animations as well.
I’ve used this sprite in a few other projects:
In this Flex-based game test
(use arrow keys to move, space to punch, doubletap arrows to run)
In my Palm Pre Accelerometer demo application
, which actually uses this same code shown here.
But enough of that, let’s write a quick HTML page to put our code to work:
<!DOCTYPE html>
<title>Canvas Spritesheet Animation</title>
<script type="text/javascript" src="js/FrameTimer.js"></script>
<script type="text/javascript" src="js/SpriteSheet.js"></script>
<script type="text/javascript" src="js/Animation.js"></script>
<script type="text/javascript">
window.onload = function() {
var timer = new FrameTimer();
timer.tick();
var sprites = new SpriteSheet({
width: 32,
height: 32,
sprites: [
{ name: 'stand' },
{ name: 'walk_1', x: 0, y: 1 },
{ name: 'walk_2', x: 0, y: 1 },
var ctx = document.getElementById('canvas').getContext('2d');
var walk = new Animation([
{ sprite: 'walk_1', time: 0.2 },
{ sprite: 'stand', time: 0.2 },
{ sprite: 'walk_2', time: 0.2 },
{ sprite: 'stand', time: 0.2 }
], sprites);
var kunioImage = new Image();
kunioImage.onload = function() {
setInterval(function(){
walk.animate(timer.getSeconds());
var frame = walk.getSprite();
ctx.clearRect(0, 0, 300, 300);
ctx.drawImage(kunioImage, frame.x, frame.y, 32, 32, 0, 0, 32, 32);
timer.tick();
}, 5);
kunioImage.src = 'img/kunio.gif';
</script>
</head>
<h1>Canvas sprite animation demo</h1>
<canvas id="canvas" width="300" height="300"></canvas>
</body>
</html>
|
<!DOCTYPE html>
<title>Canvas Spritesheet Animation</title>
<script type="text/javascript" src="js/FrameTimer.js"></script>
<script type="text/javascript" src="js/SpriteSheet.js"></script>
<script type="text/javascript" src="js/Animation.js"></script>
<script type="text/javascript">
window.onload = function() {
var timer = new FrameTimer();
timer.tick();
var sprites = new SpriteSheet({
width: 32,
height: 32,
sprites: [
{ name: 'stand' },
{ name: 'walk_1', x: 0, y: 1 },
{ name: 'walk_2', x: 0, y: 1 },
var ctx = document.getElementById('canvas').getContext('2d');
var walk = new Animation([
{ sprite: 'walk_1', time: 0.2 },
{ sprite: 'stand', time: 0.2 },
{ sprite: 'walk_2', time: 0.2 },
{ sprite: 'stand', time: 0.2 }
], sprites);
var kunioImage = new Image();
kunioImage.onload = function() {
setInterval(function(){
walk.animate(timer.getSeconds());
var frame = walk.getSprite();
ctx.clearRect(0, 0, 300, 300);
ctx.drawImage(kunioImage, frame.x, frame.y, 32, 32, 0, 0, 32, 32);
timer.tick();
}, 5);
kunioImage.src = 'img/kunio.gif';
</script>
</head>
<h1>Canvas sprite animation demo</h1>
<canvas id="canvas" width="300" height="300"></canvas>
</body>
</html>
Here it is. Our great animation system!
A live example and source code
You can see this code in action
by clicking here
As usual, don’t expect it to work in Internet Explorer. It has been tested to work in Opera 10 and Firefox 3, probably works in other canvas-supporting browsers as well.
Source:
FrameTimer
SpriteSheet
Animation
Conclusion
Animating a spritesheet with canvas is quite simple. This is why JavaScript is quickly becoming a powerful platform for small games in my opinion – very easy to use, and quite ubiquitous.
You can use this same approach to animation in other languages too – For example, the Flex example I linked uses a very similar approach.