Tuesday, December 14, 2010

Animating HTML5 Canvas Element

Hello everyone, in this blog post I will talk about animation using the HTML5 Canvas element. Canvas is an HTML5 element which is available in IE9 and can be used to create very cool animations.

IETestDrive.com features numerous Canvas animation examples such as FishIE, Mr. Potato Gun and others. I also talked about basic animation techniques for canvas in my Windows Summit 2010 Getting Started with Canvas presentation.

To illustrate different animation techniques I have created a simple canvas animation which allows the user to control the speed, trajectory and rotational velocity of the flying IE logo. You can download sample from this blog. This is how it looks:

1

I used a Butterfly curve because it looks fun and a parabolic curve because this is something you can use to simulate throwing objects or shooting objects from something like a cannon.

Let?s go through the basic animation steps used to make the IE logo fly on a specific trajectory. These are the steps:

1. I set up an animation loop: setInterval (drawTimeBasedCurve, 16)

2. Animation loop does the following:

a. Calculates X and Y coordinate of the location where I want to render my image

b. Render the image using drawImage Canvas API

c. Clears the canvas

d. Repeat loop

As you can see this is pretty simple. However let?s dig into it further. First, why are we using 16 milliseconds in the setInterval method? To create a 60 FPS animation we need to update the screen 1000 ms/60 FPS which is approximately 16 msec. So calling my draw loop every 16 ms will produce a smooth animation on a monitor with a 60 Hz refresh rate.

However there is more! Canvas can be easily manipulated by applying the transform method. Canvas transform APIs change the coordinate system of the entire canvas and therefor affect everything which is rendered on the canvas after transforms were applied. It is important to note that transforms do not change anything rendered on the canvas, instead it changes a coordinate system of the canvas only, so any graphic rendered AFTER a transform will be affected by different coordinate system changes. IE9 offloads all tranforms to the GPU, which is great for performance.

Let?s see how we can use canvas transforms to add a spinning effect to the image traveling on the specific trajectory.

First look at the default coordinate system shown in black in the figure below. The point (0,0) is in the upper left corner of the screen:

2

Now if I want to rotate the image around its center to achieve the revolution effect, I have to do following:

1. Move my coordinate system so point (0, 0) is exactly in the middle of the image I want to rotate: ctx.translate (x + imagWidth / 2, y + imgHeight / 2). Once I do it, nothing visually changes on the screen, however the invisible coordinate system used by canvas now looks like Xt and Yt shown in red

2. If I would render my object using old X, Y (from the original coordinate system) the image would move down and right. This is understandable because the new coordinate system (shown in red) moved down and right. To compensate for this, I have to adjust the coordinates to render the image at this new point: ctx.drawImage(img, -imgWidth / 2, -imgHeight / 2, imgWidth, imgHeight). Note with moving the coordinate system to the middle of the screen we have introduced negative Y and X coordinate values which now are visible on the screen.

3. Now if I rotate the coordinate system and render the image at the coordinates calculated in point 2 above, I will cause the coordinate system to look as Xr and Yr shown in green and the image will be rendered exactly in the original center (black rectangle) yet it will show rotated (green rectangle)! And here is the code to do it: ctx.rotate (distanceCurve1);

4. Since canvas transforms applies to the entire canvas, before rendering the image we need to save canvas state and restore it when done , without this each subsequent transform will be applied on top of the previous one introducing a huge mess (give it a try to see what happens):

ctx.save();

ctx.translate (x + imgWidth / 2, y + imgHeight / 2);

ctx.rotate (distanceCurve1);

ctx.drawImage(img, - imgWidth / 2, -imgHeight / 2, imgWidth, imgHeight);

ctx.restore();

The concept below may sound a bit confusing; at least it was for me. Yet once you understand canvas transforms you can do all kinds of things. In fact FishIE sample uses exactly this approach to make fish move in the fish tank.

Now let?s talk about making images fly on a parabolic curve, which is described by this formula:

y = a * x * x.

Since the coordinate system on canvas starts in the upper left corner, negative X and negative Y are off the screen and therefore not visible. Hence using formula above I can only produce half of the parabola as shown here:

3

Transforms to the rescue! I can easily move my Y axis to the middle of the screen and introduce negative X coordinate by using this transform:

ctx.translate (ctx.canvas.width / 2, 0).

And now I can easily iterate from negative X values to positive X values to create a real parabola:

4

A one more interesting detail, after each animation loop I paint the background with a .05 alpha channel and therefore create a transparent effect:

ctx.fillStyle = 'rgba(200,200,200,.05)'.

Each loop amplifies this effect and therefor creates a vanishing trail effect.

Where to go from here? You can improve the code by creating an object encapsulating all the animation logic. Then you can create an array of several objects with a different image and send these different images flying all over the screen!

Thank you and I hope you find this blog helpful.

Download code examples here!

Mia Kirshner Elisabeth Röhm Lily Allen Emmanuelle Chriqui Anna Faris

No comments:

Post a Comment