An example of HTML5 canvas animation

Recommend RGraph:  
    Read comments... Posted on 12th April 2013

Summary
A guide showing you HTML5 canvas animation using the requestAnimationFrame function. This function typically has better performance and gives better results than the setTimeout function.

Introduction

[No canvas support]

This is a demonstration of using canvas for animation. All it is is a circle bouncing around the canvas. It could be the basis of a breakout type game with more work - but that's left as an exercise for the reader! The speed of the ball is given a bit of randomness every time it reverses direction so that it doesn't continue on the same path forever and the the ball is reversed when it passes the edges of the canvas.

The newer window.requestAnimationFrame function is used to "call a new frame". This is a recent addition to browsers that is better than setTimeout and provides for higher frame rates. The target is 60 frames-per-second.

In order to account for the fact that not all browsers will have this function available a compatibility function is used to call another animation frame.


function UpdateCanvas (func)
{
    window.requestAnimationFrame =    window.requestAnimationFrame
                                   || window.webkitRequestAnimationFrame
                                   || window.msRequestAnimationFrame
                                   || window.amozRequestAnimationFrame
                                   || (function (func){setTimeout(func, 16.666);});
    
    window.requestAnimationFrame(func);
}

You can then call it like this:

UpdateCanvas(function)

You pass it the function used to update the canvas with the next frame when it's called again. Notice that there's no time parameter used - this is because the function always targets 60fps so it handles the timing itself. You just need to be concerned with updating the canvas.

Example code

The full source of the example is this:

<script>
    /**
    * The UpdateCanvas function is a wrapper around the newer requestAnimationFame function.
    */
    UpdateCanvas = function (func)
    {
        window.requestAnimationFrame =    window.requestAnimationFrame
                                       || window.webkitRequestAnimationFrame
                                       || window.msRequestAnimationFrame
                                       || window.amozRequestAnimationFrame
                                       || (function (func){setTimeout(func, 16.666);});
        
        window.requestAnimationFrame(func);
    }

    /**
    * The window.onload function starts the animation going
    */
    window.onload = function (e)
    {
        var canvas  = document.getElementById('cvs1');
        var context = canvas.getContext('2d');
        
        /**
        * This is a simple object that is drawn on to the canvas. There's nothing special - it
        * just an object that holds details of the shape that is drawn. The "type" member isn't
        * used here, but if you have different types of shape you could use it so that your
        * program knows how to draw the shape.
        */
        var shape = {
            'type':'circle',
            'x':25,
            'y':25,
            'xdir': 1,
            'ydir': 1,
            'radius': 25,
            'fill':'red',
            'stroke':'black'
        }
        var stepx = 5;
        var stepy = 5;

        /**
        * The draw function is the one called to draw a frame of the animation
        */
        function draw ()
        {
            // New frame - new day - clear the canvas
            context.clearRect(0, 0, canvas.width, canvas.height);

            /**
            * This draws the shape. Currently it only draws a circle - but could be used to draw
            * multiple shapes if we had them
            */
            switch (shape.type) {
                case 'circle':
                    context.beginPath();
                    context.fillStyle = shape.fill;
                    context.strokeStyle = shape.stroke;
                    context.arc(shape.x, shape.y, shape.radius, 0, Math.PI * 2, false);
                    context.stroke();
                    context.fill();
                    break;
            }


            /**
            * X collision detection - ie if the circle hits the sides of the canvas reverse the direction
            */
            if (( shape.x + shape.radius + stepx >= canvas.width) || (shape.x - shape.radius + stepx <= 0)) {                   
                stepx = (5 + RGraph.random(-2, 2)) * (stepx > 0 ? -1 : 1);
            }


            /**
            * Y collision detection - ie if the circle hits the sides of the canvas reverse the direction
            */
            if ( (shape.y + shape.radius + stepy >= canvas.height) || (shape.y - shape.radius + stepy <= 0)) {
                stepy = (5 + RGraph.random(-2, 2)) * (stepy > 0 ? -1 : 1);
            }
            

            shape.x += stepx;
            shape.y += stepy;


            UpdateCanvas(draw);
        }
        
        UpdateCanvas(draw);
    }
</script>

Comments