Animate a random bouncing ball with the <canvas> tag

August 6th, 2010

I’ve been experimenting with the <canvas> tag the last few days and from what I’ve seen, it’s got potential but it’s got a ways to go before we’re replacing Flash with it. But, in order to learn how to use it, I’ve been trying to reproduce things I can remember making when I was first learning Flash and ActionScript, and one of those things was a bouncing ball who’s movements where controlled through code.

To start off, here’s the HTML:

1
<canvas id="canvas" width="400" height="300"></canvas>

You have to assign the height and width of the <canvas> tag in the HTML because the default height is 150 pixels and the width is 300 and if you set the dimensions with CSS, the dimensions of the content inside the <canvas> will skew to the ratio of the CSS dimensions. For example, if you set the width to 600 pixels in the CSS and make a rectangle that’s 100 pixels wide, it will display at 200 pixels wide because you’ve double the width of the <canvas> with CSS. We also need to give the <canvas> an id of “canvas” so we can reference it with our JavaScript.

So for CSS, all I’ve included is this:

1
2
3
#canvas {
  border:1px dashed black;
}

It’s just there so that I can see the edges of the <canvas>.

JavaScript is what powers the <canvas> and to make this work, it takes about 35 lines of JS, not that much.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
var canvasWidth = 400,
    canvasHeight = 300,
    ball_x = 5,
    ball_y = 44,
    ball_dx = 5,
    ball_dy = 5;
function init() {
  var canvas = document.getElementById('canvas');
  context = canvas.getContext('2d');
   
  setInterval('draw();', 25);
}
function clearCanvas() {
  context.clearRect(0,0,canvasWidth,canvasHeight);
}
function ball(x,y,r) {
  context.fillStyle = '#f00';
  context.beginPath();
  context.arc(x,y,r,0, Math.PI * 2, true);
  context.fill();
}
function draw() {

  clearCanvas();
 
  ball(ball_x, ball_y, 10);
 
  if (ball_x + ball_dx > canvasWidth || ball_x + ball_dx < 0) {
    ball_dx = -ball_dx;
  }
  if (ball_y + ball_dy > canvasHeight || ball_y + ball_dy < 0) {
    ball_dy = -ball_dy;
  }
 
  ball_x += ball_dx;
  ball_y += ball_dy;
}

So what are we doing here? Well, the first thing we do is define our variables:

1
2
3
4
5
6
var canvasWidth = 400,
    canvasHeight = 300,
    ball_x = 5,
    ball_y = 44,
    ball_dx = 5,
    ball_dy = 5;

Ball_x and ball_y are our ball’s initial position and ball_dx and ball_dy are the speed our ball will be travelling.

1
2
3
4
5
6
function init() {
  var canvas = document.getElementById('canvas');
  context = canvas.getContext('2d');
   
  setInterval('draw();', 25);
}

In our init() function, we use the getElementById to find our <canvas> and then we set the context to 2D because right now that’s all you can do, although, one day we’re supposed to be able to set the <canvas> to 3D. Next, setInterval is what we use to actually make the animation fire. It will called the function draw() and it will fire every 24 milliseconds.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function draw() {

  clearCanvas();
 
  ball(ball_x, ball_y, 10);
 
  if (ball_x + ball_dx > canvasWidth || ball_x + ball_dx < 0) {
    ball_dx = -ball_dx;
  }
  if (ball_y + ball_dy > canvasHeight || ball_y + ball_dy < 0) {
    ball_dy = -ball_dy;
  }
 
  ball_x += ball_dx;
  ball_y += ball_dy;
}

What we’re doing in the draw() function is redrawing the ball every time the function is fired. In the if statements, we see if the ball has reached the edge of the <canvas> and if it has, we reverse it’s direction. To redraw the ball, we call the ball() function by passing in 3 values, the ball’s x and y positions and the ball’s radius.

1
2
3
4
5
6
function ball(x,y,r) {
  context.fillStyle = '#f00';
  context.beginPath();
  context.arc(x,y,r,0, Math.PI * 2, true);
  context.fill();
}

So when we pass in the new values, the ball() function will redraw the ball in it’s new position. The fillStyle sets the color to red and arc() sets the position, the radius, the starting point, Math.PI * 2 sets the arc to a full circle and then true or false sets clockwise or counter-clockwise.

In the draw() function, you might have noticed a function called clearCanvas() being called. One of the big differences between the <canvas> and the stage for Flash is that the <canvas> is actually redrawing itself every time the setInterval fires and it won’t remove the old ball position. So, in order to get the effect we’re looking for, we need to clear the <canvas> every time the ball moves.

1
2
3
function clearCanvas() {
  context.clearRect(0,0,canvasWidth,canvasHeight);
}

To do this, we call the clearRect() function where we set the position to 0,0 and set the width and height to width and height of the <canvas>. This way, we will clear the <canvas> every time we redraw the ball which will make it look like the ball is bouncing around. If we don’t do this, then the ball won’t be removed and eventually the entire <canvas> will fill up.

Finally to make the JavaScript run, we need to add this to the body tag:

1
<body onload="init();">

Now, after the page loads, it will run the init() function. You can also attact this to a button or a link if you want to start the animation that way.

Check out the demo.

So far, all the most complicated <canvas> examples I’ve seen have been done by hardcore programmers and are too complex to be something that people can realistically build for clients or even for fun. I think for <canvas> to really take off someone like Adobe is going to need to come out with an IDE that will do most of the heavy lifting for us. But for now, we can get started with things like this.

5 Responses to Animate a random bouncing ball with the <canvas> tag

  1. Scott VR says:

    OK Once more and I hope you delete my previous comments with the garbled code example..

    I just wanted to point out that in your draw() function where you check to see if the ball is out of bounds, you are checking for ball_x + dx and ball_y +dy; what you meant to do, since ball_x, ball_y is the center of the ball, is check for a distance of the ball’s radius from the center of the ball. A more accurate way to check for a boundary collision would be (since your radius is 10 in your demo use ’10′, not d_x and d_y):

    1
    2
    3
    4
    5
    6
    if (ball_x + 10 > canvasWidth || ball_x - 10  < 0) {
        ball_dx = -ball_dx;
    }
    if (ball_y + 10 > canvasHeight || ball_y - 10 < 0) {
        ball_dy = -ball_dy;
    }

    Also, you’ll want to make sure that you don’t start the ball off where it’s halfway off the screen (already collided withthe boundary, thus the boundary check will keep returning true!) so set ball_x to something like 15 to start with instead of 5.

    Hope this is helpful.

  2. Scott VR says:

    One more thing.. Instead of clearing the entire canvas, just clear the circle where it was drawn before updating dx an dy.. You could add another argument to your ball function that takes a color.. draw it once with white (to erase), then update d_x and d_y, then draw it again in red. This way you are only painting an area of approximately 314 pixels instead of 120,000 pixels every time your loops runs.

    Sorry I had to paste the previous message so many times to get it right. There was no preview button and I don’t often post to blogs so I wasn’t sure how to format the code example.

  3. Mike says:

    Scott,

    This was very early in my experimentation with canvas and I’d probably do it differently now. Thanks for the tips!

  4. Scott VR says:

    Cool. I also realized we could save ourselves the arithmetic in the loop by declaring variables outside the loop containing canvasWidth – 10 and canvasHeight – 10 instead of adding 10 to the ball_x and ball_y values inside the loop and doing our comparisons against those.

    I converted a test page I had written that animated using CSS to use the canvas after reading your page and the thoughts I shared with you to optimize it were things that I thought of directly as a result of reading your post. So thanks for the original article.

  5. Hey, mind if I bend your ear a bit? I have something important that you might like :-) My buddies and I (we’re from Florida) have been watching this blog for a while and we really like it so we decided to share something with you that’s been working for our websites. I’m not sure if you knew this, but building an email list is HUGE. You can increase your visitors, and if you monitize them correctly, they can be a serious gold mine. There is a lot I would love to talk to you about that you can do to increase your revenue here. However, I won’t do it justice. Just go here instead: http://www.make1150weekly.info/ and check out what these crazy guys are doing. Personally, I have revamped all of my sites (didn’t take much time at all) and I’m making a steady $200-$400 DAILY, swear on my mother I made $274 yesterday from blogs. Amazing huh? Just a couple weeks ago, I was happy scraping by with 6 bucks a day on adsense heh. Check it out, really. You’ll be doing yourself a huge favor.

Leave a Reply

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

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>