An example of canvas animation

Written by Richard Heyes, on 12th April 2013

A guide showing you canvas animation using the requestAnimationFrame() function. This function typically has better performance and gives you 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 ball is reversed when it hits 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 you with higher frame rates. The target is 60fps.

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.mozRequestAnimationFrame
                                   || (function (func){setTimeout(func, 16.666);});
    
    window.requestAnimationFrame(func);
}

You can then call it like this:

UpdateCanvas(function)

You pass it the function that's 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.mozRequestAnimationFrame
                                       || (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's drawn on to the canvas. There's nothing special - it's
        // just an object that holds details of the shape that's 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 ()
        {
            // A new frame so 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;
            }


            // Horizontal 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>