The HTML 5 Canvas - Good Clean Fun
Posted by Tom on 2009-08-11 20:25
So the plan was it open this weblog with a deep and illuminating post. Something .NETty and architectural in nature. But then I got distracted by shiny things and so instead I bring you something completely different: silly baubles.
The <canvas> element is introduced in HTML 5 and has a very simple premise: it gives you a surface you can draw to on the client side using Javascript. Slinging one of these into your HTML is easy enough.
<canvas id="MainCanvas" width="300" height="300" />
But of course the fun really starts outside the markup, where the wild things are.
var context = canvas.getContext('2d');
The canvas object is the access to the canvas element in the DOM, and does little else beyond exposing the attributes of the canvas tag. The fun really starts when you get your grubby little mits on the context. You get some simple vector drawing functions, allowing you to draw lines, rectangles, arcs, and bezier curves if you're nasty, along with a series of fill options such as gradient and patterns. I'll not go into the details because I can't be bothered and you can just look it up here. Time for shinys!
Balls . . . heh heh
I love particle systems to an almost unhealthy degree so here's a load of bouncing balls. I was going to make this a lot more involved in terms of using the features of canvas, but then I got distracted by mechanics and took a sharp left at the junction of Newton and Collision Response and ended up in the bad side of town where the physicists hang out. Dang.
So yeah, not a lot to be said for this one. It's mostly badly faked collision model and you could actually do this in non-canvas, venerable DHTML really easily. This one is mostly an exercise in how to set up the animation using setInterval and the like.
The only real thing of some note in here is some cheap motion blur. Rather than clear the frame every time I'm simply drawing over the entire canvas with a rectangle that's the same colour as the background but with a low alpha (or opacity, if that terminology seems more familiar to you.) This will work fine unless you're using the draw order to depth-cue your objects (if you need an object to look like it's 'behind' another by drawing the further object before the nearer one).
This second one is a lot more feature-filled, since it touches on transformations and the transformation stack and patterns and multiple canvas and alpha blending and stuff.
First the blocky image is drawn into a canvas, and a pattern is created from it. This can then be passed into the fillStyle property of the context and will be tiled across any filled object you want to render. In this case a BIG DAMN SQUARE, because I'm interesting like that.
Also on this one you can see an example of transformations. The transformation are cumulative and non-associative - so a rotation of X followed by a translation of Y is not the same as a translation of Y followed by a rotation of X. A transformation stack is available and the concept will be familiar to anyone whose has used a graphics API in the past. If you can't see a use for one then I wouldn't sweat it at the moment. Suffice to say that the stack allows you to save the current set of transformations, and retrieve them again to roll the transformation back to a previous state.
So what?
It's a valid question.
Canvas is interesting stuff, but one thing it's not is a 'Flash killer'. Flash's strength lies in its animations and its vector drawing, whereas canvas will only give you the most primitive of primitives. (it's got support for the Javascript Image object, but that's hardly the same thing.) Oh, at some point some crazy awesome loon with too much time on their hands will create some magical things with it - because we're geeks and we're prone to do crap like that, but it's a poor fit for any real drawing or animation work. Odd little games are fun and all but it's always a shame to have something this slick and not have an excuse to use it at work. Maybe I'm being horrifically myopic (it wouldn't be the first time,) but I am struggling to think of practical things to do with it besides dynamic graphs. It's a Monday though, and I've had no bourbon, so I'm not feeling too creative.
But there could well be more - the astute among you will have noticed that we're passing an argument to the getContext call. The fact that you have to ask for '2d' now implies that '3d' may be available later. It's not part of the spec, but that hasn't stopped people running with it. Firefox currently has an extension that ties to OpenGL ES 2.0 (shaders and everything!) and Opera has included their own take on the 3D canvas natively. Could well be an interesting time for browser games in the next couple of years.
Finally, if you want to see some genuinely dazzling work in this realm, one doyen I was introduced to on my travels is Mathieu 'P01' Henri, who can make Javascript dance like Fred Astaire on a hot plate. You should really do yourself a favour and have a look.
Generic arse-covering: The code itself is probably not great. I know enough Javascript to get by but have no idea, for example, how much overhead is associated with creating objects via literal notation inside a loop. Also some of it is just plain, common-or-garden laziness. There's no checks for divide-by-zeros and there's bound to be other things I haven't tested thoroughly. It illustrates the point though. As always, use at your own risk and don't treat is the paragon of Javascript or anything.