Simple Animation in the HTML5 Canvas Element

HTML5 is generating all kinds of buzz these days. Some of the buzz is about HTML5 being a replacement for Adobe’s Flash. I don’t think it’s there yet but it’s certainly on the way to changing the way content is presented on the web. This is a description of a very simple animation in an HTML5 canvas element. It is coded for readability and not for optimized operation.

We’ll add a canvas element to a web page and then use javascript to draw on it. We will redraw it all every 10 milliseconds with minor changes to create the magical illusion of animation. Using javascript allows all the redrawing to occur in the browser as opposed to on the server which would require communication between the browser and server.

I cannibalized much of this code from Bill Mill’s excellent canvas tutorial at…

http://billmill.org/static/canvastutorial/
If you find this post interesting, then I highly reccommend visiting his tutorial next. It creates a breakout game and is fully explained with incremental steps.

Here is the code for the page we will be discussing.


<!doctype html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Canvas Test</title>
  </head>
<body>
  <section>
    <div>
        <canvas id="canvas" width="400" height="300">
         This text is displayed if your browser 
         does not support HTML5 Canvas.
        </canvas>
    </div>

<script type="text/javascript">
var canvas;  
var ctx;
var x = 400;
var y = 300;
var dx = 2;
var dy = 4;
var WIDTH = 400;
var HEIGHT = 300; 

function circle(x,y,r) {
  ctx.beginPath();
  ctx.arc(x, y, r, 0, Math.PI*2, true);
  ctx.fill();
}

function rect(x,y,w,h) {
  ctx.beginPath();
  ctx.rect(x,y,w,h);
  ctx.closePath();
  ctx.fill();
}

 
function clear() {
  ctx.clearRect(0, 0, WIDTH, HEIGHT);
}

function init() {
  canvas = document.getElementById("canvas");
  ctx = canvas.getContext("2d");
  return setInterval(draw, 10);
}


function draw() {
  clear();
  ctx.fillStyle = "#FAF7F8";
  rect(0,0,WIDTH,HEIGHT);
  ctx.fillStyle = "#444444";
  circle(x, y, 10);

  if (x + dx > WIDTH || x + dx < 0)
    dx = -dx;
  if (y + dy > HEIGHT || y + dy < 0)
    dy = -dy;

  x += dx;
  y += dy;
}

init();
</script>

  </section>
</body>
</html>

You can copy this code and paste it into a new file called something like canvastest.html and when you open it with an HTML5 browser like Firefox 3.6 or Google Chrome 5.0 it will display the animation.


The canvas element is part of HTML5 and is currently described by the W3C as

“A canvas interface element represents a resolution-dependent bitmap canvas, which can be used for rendering graphs, game graphics, or other visual images dynamically.
The definition of a canvas interface element in a host language must have a means for an author to control the width and height of the coordinate space, such as width and height attributes. These width and height attributes, when specified, must have values that are valid non-negative integers.”

http://dev.w3.org/html5/canvas-api/canvas-2d-api.html

It looks like this


<canvas id="canvas" width="400" height="300">
This text is displayed if your browser does not support HTML5 Canvas.
</canvas>

As required by the W3C Spec, it has width and height attributes and we set them to valid non-negative integers. It also has the ability to present output to browsers that do not support the canvas tag. In this example it will output
“This text is displayed if your browser does not support HTML5 Canvas.”
to browsers that do not support the canvas element.

To draw on our canvas we need to get a javascript reference to the canvas element. We do this in our init() function which is the function that we call to start everything.


function init() {
  canvas = document.getElementById("canvas");
  ctx = canvas.getContext("2d");
  return setInterval(draw, 10);
}

First we get the element ID from the DOM for the canvas element with the id = “canvas” and then we get an object in the form of a context of our canvas allowing us to do stuff with it in javascript.


  canvas = document.getElementById("canvas");
  ctx = canvas.getContext("2d");

The context is “2d” There is a “3d” context being developed but it isn’t as standardized as the “2d” context is at this time.

The “2d” context interface can be found here. http://dev.w3.org/html5/canvas-api/canvas-2d-api.html#canvasrenderingcontext2d

This context contains the basic methods for drawing on the canvas such as arc(), lineto() and fill().

The next line uses setInterval(function, time) to call the draw() function every 10 milliseconds. The setInterval() method calls a function or evaluates an expression at specified intervals (in milliseconds).

The draw() function is the heart of our script and it beats once every 10 milliseconds thanks to the setInterval() method.


function draw() {
  clear();
  ctx.fillStyle = "#FAF7F8";
  rect(0,0,WIDTH,HEIGHT);
  ctx.fillStyle = "#444444";
  circle(x, y, 10);

  if (x + dx > WIDTH || x + dx < 0)
    dx = -dx;
  if (y + dy > HEIGHT || y + dy < 0)
    dy = -dy;

  x += dx;
  y += dy;
}

First we call our clear() function which uses the clearRect() method to erase everything in a rectangle that is the same size as the canvas. Two opposite corners of the rectangle to be cleared are set as (0,0) and (WIDTH, HEIGHT). The WIDTH and HEIGHT have the same value as the width an height of the canvas. You could just as easily only clear part of the canvas but we want to clear all of it.

function clear() {
  ctx.clearRect(0, 0, WIDTH, HEIGHT);
}

After we clear the canvas the fillstyle for the context is set to the color that we want the canvas background to be.

ctx.fillStyle = "#FAF7F8";


Next we create a rectangle that is the same size as the canvas using our rect() function.


function rect(x,y,w,h) {
  ctx.beginPath();
  ctx.rect(x,y,w,h);
  ctx.closePath();
  ctx.fill();
}

The fill method fills the rectangle with the color we defined in the fillStyle attribute. The fillstyle attribute can be set to a css color string such as hex like #FFFFFF or rgba() like rgba(255,255,255,0) there are more options such as gradients.
http://dev.w3.org/html5/canvas-api/canvas-2d-api.html#dom-context-2d-fillstyle

Now back in the draw() function we change the fillStyle attribute to the color that we want our circle to be.


  ctx.fillStyle = "#444444";

In our example we will be creating a circle which will move around the canvas in a straight line and bounce off the edges of the canvas. To create our circle we will make a circle function.


function circle(x,y,r) {
  ctx.beginPath();
  ctx.arc(x, y, r, 0, Math.PI*2, true);
  ctx.fill();
}

The beginpath() method makes sure that we are starting a new path.
Next we use the arc(x, y, radius, startAngle, endAngle, anticlockwise) method to define the size and shape of our circle. It is defined as

“The arc(x, y, radius, startAngle, endAngle, anticlockwise) method draws an arc. If the context has any subpaths, then the method must add a straight line from the last point in the subpath to the start point of the arc. In any case, the arc() method must draw the arc between the start point of the arc and the end point of the arc, and add the start and end points of the arc to the subpath. The arc and its start and end points are defined as follows:
* Consider a circle that has its origin at (x, y) and that has radius radius. The points at startAngle and endAngle along this circle’s circumference—measured in radians clockwise from the positive x-axis—are the start and end points, respectively.”

http://dev.w3.org/html5/canvas-api/canvas-2d-api.html#dom-context-2d-arc

So in our circle function we enter the x,y coordinates of the center of our circle and the radius to calculate the size of the circle. Our start angle is 0 radians (0 degrees) and our end angle is 2*pi radians(360 degrees) giving us a complete circle.

Next we use the fill() method which fills the closed path (our circle) with the color defined by the value of the fillstyle attribute of the context object.

Our circle is defined by

circle(x, y, 10);
It has a radius of 10 and its origin is at (x,y). To move the circle, we just need to change x and y. The variables dx and dy determine how much to change x and y by each time we execute the draw() function (once every 10 milliseconds). We change x by the value of dx as long as x + dx is not greater than the width of the canvas or less than 0. If it is then we reverse it by making dx = -dx. We do the same for y + dy.


  if (x + dx > WIDTH || x + dx < 0)
    dx = -dx;
  if (y + dy > HEIGHT || y + dy < 0)
    dy = -dy;

  x += dx;
  y += dy; 

Here is our canvas in action…


This text is displayed if your browser does not support HTML5 Canvas.


Now you can change the values and play with the code. Make the circle bigger or smaller. Make it travel along a sine wave. Make it change color. Have fun with the code as that is the easiest way to learn.

Please leave a comment if you have any questions or corrections.


13 Replies to “Simple Animation in the HTML5 Canvas Element”

  1. Thanks! I took this to computer camp and we were able to change it and make it different. We use Google chrome browser and it works on that.

  2. Thanks for sharing this.

    I’ve been trying to work out how to do multiple circles bouncing around but this will only work if I declare global variable x,y,dx,dy for each circle I create. If I create 50 circles, this method is not feasible.

    Is there anyway to do this without global variables and have different dx, dy values for each circle so it bounces around randomly?

  3. @Quang
    There are at least three ways to do that (I am sure there are more than that).

    1. Use an array for the x, y, dx, and dy such as x(1), y(1), dx(1), dy(1), x(2) ….

    2. Create circle objects that each have x, y, dx, and dy properties.

    3. Use multiple canvases with one circle on each canvas.

    In my examples, I do not stress good practices for globals because I am trying to make it as simple as possible to express the basic concepts. I highly recommend the book Javascript Patterns ISBN 978-0-596-80675-0 for learning best practices with variables. It also discusses the pattern you would use to create multiple circle objects.

  4. “Could you please show how to implement the code to create an array for multiple balls?”

    This post might help you get started…
    http://html5.litten.com/using-multiple-html5-canvases-as-layers/

    “How could you make that ball clickable. For example open an alert box when clicked?”

    If you use multiple layers, then you can have better control over capturing click events on specific objects drawn on the canvases. You could make the individual canvases be the size of the object you want to catch clicks on and simply watch for clicks on the canvas or you could check the position of the mouse when clicking and test to see if the object you want to see clicked is at the same location as the mouse.

    Hope this helps
    James

  5. How do I make a segment move with time. I mean the segment is at (100,100) initially and then it has to move horizontally to (150,100) after 2seconds. Please help

  6. Please, may you can help because I am in a hurry

    Does the context keeps previous data?
    if I make an arc with all its params
    then next circle I omit some of them
    will the context work with the previous ones
    lets say to draw same arc just another radius… or have to use all params again?
    Thanks
    Please, email me If you can =)

  7. How make the circle bigger or smaller after a reflection of the wall?
    Please code with example! 🙂
    I can change color but I have problem with size.

Leave a Reply to Rahul Cancel reply

Your email address will not be published. Required fields are marked *