Free Trial

Safari Books Online is a digital library providing on-demand subscription access to thousands of learning resources.

  • Create BookmarkCreate Bookmark
  • Create Note or TagCreate Note or Tag
  • PrintPrint
Share this Page URL
Help

4. Hacking Your Graphics with Canvas and... > Hack 41. Accelerate Animation with C...

Hack 41. Accelerate Animation with Canvas Drawings

Use of the <canvas> tag is often one of the most efficient ways to create animations in your web applications. This hack digs into the nitty-gritty of creating animations while using the <canvas> tag.

Clean animation can make or break your web applications. Native applications on desktop and mobile devices have raised users’ expectations: if your web application fails to include clean, concise animations, users will often write it off as being a poorly performing app.

Canvas animation can be a powerful tool for web animations. As more and more browser makers enable the GPU for canvas animations, it becomes even more beneficial to perform your animations with a canvas element.

Write and Clean

Animation on a <canvas> tag is reminiscent of early cartoon animations where each frame is drawn out and then displayed in the correct order and at the determined frame rate. Canvas animation basically consists of these three steps:

  1. Draw on the canvas.

  2. Erase what you just drew.

  3. Repeat steps 1 and 2 until the animation is complete.

In JavaScript, when things need to be called over and over again we often use methods such as setTimeout and setInterval to call our drawing methods. The problem with each of these methods is they need to be set to a specific amount of time. If we set that time to, say, 100 milliseconds, we would never be able to achieve a frame rate higher than 10 frames per second.

A powerful new standard has been introduced to address this issue with the <canvas> tag: the requestAnimationFrame method. With this method, you are asking the browser to render the next frame as soon as it is available for rendering, as opposed to attempting to render at a fixed interval. The goal of requestAnimationFrame is 60 frames per second, but it doesn’t fail if it can’t render that quickly; it simply renders as soon as it can. Note that this method isn’t limited to use in canvas animations; it’s available for any web drawing technology, including WebGL.

Smile, You’re Being Animated!

Let’s take a good look at an example of a canvas animation. If you’ve worked your way through the previous hacks in this chapter you have seen the smiley face examples. Each example drew the smiley face on a 200 × 200 canvas element. For this illustration we will draw it on a much larger canvas to give us room to move. Let’s start by dropping our <canvas> tag onto the page:

<canvas id="moveSmile" width="800" height="200"></canvas>

Now that we have a big, fat, blank canvas, we will draw the smiley face on top of it. To do this, we’ll pull in a few lines of JavaScript to build our page elements:

       var canvas  = document.getElementById("moveSmile");
       var smileCtx = canvas.getContext("2d");

        smileCtx.beginPath();

        smileCtx.fillStyle = '#F1F42E';
        smileCtx.arc(100,100,99,0,Math.PI*2); // head

        smileCtx.stroke();
        smileCtx.fill();

        smileCtx.beginPath();
        smileCtx.moveTo(170,100);
        smileCtx.arc(100,100,70,0,Math.PI);   // Mouth
        smileCtx.stroke();

        smileCtx.beginPath();
        smileCtx6.fillStyle = 'black';
        smileCtx6.moveTo(60, 65);
        smileCtx6.arc(60,65,12,0,Math.PI*2);  // Left eye
        smileCtx6.fill();

        smileCtx6.beginPath();
        smileCtx6.moveTo(140,65);
        smileCtx6.arc(140,65,12,0,Math.PI*2);  // Right eye
        smileCtx6.fill();

Our code simply draws out this smiley face on the lefthand side of the <canvas> tag. For illustration purposes, a 1 px border has been added to the <canvas> tag so that we can see the boundaries (see Figure 4-15).

The smiley face illustration on the lefthand side of an 800-point canvas
Figure 4-15. The smiley face illustration on the lefthand side of an 800-point canvas

Going back to our three-step process, once we draw our illustration we need to erase what we’ve drawn:

smileCtx.clearRect(0, 0, 800, 200); //smileCtx is the 2d context

For simplicity I’m erasing the whole canvas, but to optimize performance you should focus on erasing what is changing for the next frame. In the preceding method I am clearing the whole canvas by setting the clearRect coordinates from the top-lefthand corner of the canvas to the bottom-righthand corner. This erases a rectangular shape the size of the canvas.

Our canvas should now be void of illustration, as shown in Figure 4-16.

The 800 × 200 <canvas> tag after the clearRect method has cleared the entire canvas context
Figure 4-16. The 800 × 200 <canvas> tag after the clearRect method has cleared the entire canvas context

Now, for step 3 we will redraw our smiley face, but we will move it slightly to the right. In order to do this, we will move the x position of both our moveTo methods and our element start position (arc in this case).

To accomplish this, we will replace each number with a simple equation to generate the proper x coordinate each time the element is drawing:

    x+startingposition

Our code will now look like this:

        var x = 0;
        smileCtx6.beginPath();

        smileCtx6.fillStyle = '#F1F42E';
        smileCtx6.arc(x+100,100,99,0,Math.PI*2); // head

        smileCtx6.stroke();
        smileCtx6.fill();

        smileCtx6.beginPath();
        smileCtx6.moveTo(x+170,100);
        smileCtx6.arc(x+100,100,70,0,Math.PI);   // Mouth
        smileCtx6.stroke();

        smileCtx6.beginPath();
        smileCtx6.fillStyle = 'black';
        smileCtx6.moveTo(x+60, 65);
        smileCtx6.arc(x+60,65,12,0,Math.PI*2);  // Left eye
        smileCtx6.fill();

        smileCtx6.beginPath();
        smileCtx6.moveTo(x+140,65);
        smileCtx6.arc(x+140,65,12,0,Math.PI*2);  // Right eye
        smileCtx6.fill();

For the preceding code x is set to 0, but in order to move the smiley face across the screen we need to change the x position. We’ll do this with a simple statement that increases or decreases the x value appropriately (this will move it across the screen and then back again).

There is one additional value we need to determine: the speed of the animation. If we simply increment the value by 1, the smiley face will only move one pixel per iteration. We want to put a little bit of pep in this animation, so we will create a new variable called speed and set it to 6. When this number is added to the current x position, it will move the smiley face forward or back six pixels, thus increasing the speed. Let’s look at the code:

var speed = 6; //px it moves on each loop determines how fast it moves

 x += speed;

if(x <= 0 || x >= 600){ //as far as we can go without cutting off
    speed = -speed;  //determines if it moves forwards or backwards;
}

Implementing requestAnimationFrame

As mentioned earlier, requestAnimationFrame is a new specification in the HTML5 family. It’s so new that most browsers only support a prefixed version of it. In order to utilize it in modern browsers, we need to do a quick check to see which version of the method we need to use, and then build a reference to it.

We will use the requestAnimationFrame method in our example to iterate through our animation. To accomplish this, we will use it to call the same draw method cyclically. Remember, the frame rate will be determined by requestAnimationFrame, as it will call the draw method as soon as the browser is ready to draw another screen.

Putting It All Together

The requestAnimationFrame method is really the glue that holds this example together. To get everything working properly, we will set our variables at the top of our page and then break our code into two methods. The first will determine the new x value and then call the draw method.

The draw method will first clear the canvas from the previous frame and then draw out the new frame. This method gets called over and over again. Our final code assembles into this:

var x =  0;
var speed = 6; //px it moves on loop determines how fast it moves
var canvas  = document.getElementById("moveSmile");
var smileCtx = canvas.getContext("2d");

function animate(){

   reqAnimFrame = window.mozRequestAnimationFrame||window.webkitRequestAnima
tionFrame
 ||window.msRequestAnimationFrame||window.oRequestAnimationFrame
        reqAnimFrame(animate);

        x += speed;

        if(x <= 0 || x >= 600){
          speed = -speed;  //see if it moves forwards or backwards;
        }

        draw();
    }


    function draw() {

        smileCtx6.clearRect(0, 0, 800, 200);

        smileCtx6.beginPath();

        smileCtx6.fillStyle = '#F1F42E';
        smileCtx6.arc(x+100,100,99,0,Math.PI*2); // head

        smileCtx6.stroke();
        smileCtx6.fill();

        smileCtx6.beginPath();
        smileCtx6.moveTo(x+170,100);
        smileCtx6.arc(x+100,100,70,0,Math.PI);   // Mouth
        smileCtx6.stroke();

        smileCtx6.beginPath();
        smileCtx6.fillStyle = 'black';
        smileCtx6.moveTo(x+60, 65);
        smileCtx6.arc(x+60,65,12,0,Math.PI*2);  // Left eye
        smileCtx6.fill();

        smileCtx6.beginPath();
        smileCtx6.moveTo(x+140,65);
        smileCtx6.arc(x+140,65,12,0,Math.PI*2);  // Right eye
        smileCtx6.fill();
    }

    animate();

Figure 4-17 shows a snapshot from our example. Our smiley face starts at the far-left side of the canvas element, and then animates to the far-right side. It will then repeat this step over and over again.

A frame from the smiley face animation showing the smiley face moving from one side of the canvas element to the other and back again
Figure 4-17. A frame from the smiley face animation showing the smiley face moving from one side of the canvas element to the other and back again
  • Safari Books Online
  • Create BookmarkCreate Bookmark
  • Create Note or TagCreate Note or Tag
  • PrintPrint