HTML5’s Canvas and CanvasRenderingContext2d bring something to HTML5 that has long been missing from HTML: the ability to draw pixels into a browser window. I wrote about the canvas API in a previous post entitled Making HTML5 Come Alive with the Canvas API. This time, I’d like to build on that post by exploring a subject for which HTML5 provides no native support: canvas animations.
Silverlight has a robust animation engine built in that makes it easy to make objects move around the screen. HTML5 does not, but you can use timer functions to move objects around by repositioning them at regular intervals. Because HTML5’s canvas API is an immediate-mode drawing API (as opposed to Silverlight’s retained-mode API), moving an object from one position to another requires you to erase it from its current position before drawing it in its new position. Erase, draw, repeat – that’s the basic technique for animating movement within a canvas in HTML5. But as you’ll see, it’s possible to animate CSS properties of the canvas itself to lend the whole affair a retained-mode feel.
For a demonstration, point an HTML5 browser such as Internet Explorer 9, Firefox 4, Chrome 10, Opera 11, or Safari 5 to http://www.wintellect.com/html5/MovingAnimation1.html. You’ll see a page containing a red ball that measures 100 pixels in diameter bouncing around in a 400x400 canvas:
Here’s the code that animates the red ball. Every 10 milliseconds or so, the move function erases the ball by calling CanvasRenderingContext2d’s clearRect method. Then it computes the ball’s new position and draws a red circle there. Like frames in a video, it creates the illusion of motion:
// Set a timer to fire every 10 milliseconds
setInterval("move()", 10);
function move() {
// Erase the circle from its old position
_dc.clearRect(_x – 50, _y – 50, 100, 100);
// Compute the circle's new position
var x = _x + _dx;
if (x < 51 || x > (_cx - 51)) {
x = _x - _dx;
_dx = -_dx;
}
var y = _y + _dy;
if (y < 51 || y > (_cy - 51)) {
y = _y - _dy;
_dy = -_dy;
}
_x = x;
_y = y;
// Redraw the circle in its new position
_dc.fillStyle = "red";
_dc.beginPath();
_dc.arc(_x, _y, 50, 0, Math.PI * 2, true);
_dc.fill();
}
Nothing hard about that. And one of the nice things about clearRect is that works equally well against complex backgrounds. To see what I mean, check out the page at http://www.wintellect.com/html5/MovingAnimation2.html. Unlike its predecessor, it animates a ball against a background consisting of a wood-grain image. For good measure, it also uses a radial gradient fill to make the ball look a little more realistic:
While there’s nothing inherently wrong with animating an object this way, developers used to retained-mode graphics subsystems will come away feeling a little cold. Wouldn’t it be nice if there were a way draw the ball once and then move it around the screen without having to continually erase and redraw it? Funny you should ask. Because with a little help from CSS, you can do just that.
The third example in this series – http://www.wintellect.com/html5/MovingAnimation3.html – does things a little differently. It looks the same on the outside, but on the inside, instead of erasing and redrawing a ball in a 400x400 canvas, it draws the ball once in a 100x100 canvas. Then it uses a timer function to animate the canvas’s CSS left and top properties. Now the browser takes care of doing the erasing and redrawing. Here’s the modified move function:
function move() {
// Get the ball's current position
var left = _canvas.style.left;
var top = _canvas.style.top;
var x = parseInt(left.substring(0, left.length - 2));
var y = parseInt(top.substring(0, top.length - 2));
// Compute the ball's new position
x += _dx;
if (x < 0 || x > (_cx - 101)) {
_dx = -_dx;
x += (2 * _dx);
}
y += _dy;
if (y < 0 || y > (_cy - 101)) {
_dy = -_dy;
y += (2 * _dy);
}
// Move the ball to its new position
_canvas.style.left = x + "px";
_canvas.style.top = y + "px";
}
Animating the canvas position rather than animating the position of something within the canvas is a huge step in the right direction. And it has the added advantage that it allows the browser to use the GPU to move pixels around the screen – something the browser cannot do when your code is continually erasing and redrawing. Keep this in mind when building animations in HTML5.
On Apr 3 2011 4:36 PMBy jprosise
Ugh. immediate mode rendering in 2011? Please. One of many reasons I hope HTML 5 fails.
Yep, immediate mode it is. So primitive. But great for devs who are being paid by the hour!
If it's retained mode you want, have two canvas objects -- one painted with the background, and one painted with the ball; then move the ball canvas around by tweaking it's CSS properties.
If I'm not mistaken, all the major browsers fully support partially-transparent canvases, so it should work just fine; and should also benefit from full hardware acceleration during the composition process.
Timothy, you are so right. I revised the post to do away with the kludgy image painting and to show how to use CSS to animate the ball. Thanks for making me rethink the best way to do this!
Then what's the point of using HTML5 (or canvas) ?
It's no different than a simple DOM & CSS animation right ?
SVG allows for the moving of objects directly ... unfortunately IE9 does not support the animation tags so scripts still need to be used. SVG object can have events associated with them such as mouse over, etc, etc.
Between methods for Canvas; ultimately what renders fastest in the browsers and scales best will be the best method.
Yep, I'm actually doing some work with SVG animations right now. They don't work in IE9 and they don't work in Chrome, either. They work pretty well in Firefox, however.
Game Loops and Timer-Based Animations Previous Entries in Series Part 1 – Introduction and Game Layout
The CSS trick is genius! Thank you for posting.
I'm trying to get your first example above working, but can't seem to manage it with just this code fragment, and when I try to look at the full example here:
http://www.wintellect.com/html5/MovingAnimation1.html
The link is broken....
Is it available elsewhere?