Impressing users with animation involves more than knowing how to move objects—you also need to know how to move them in ways that users expect. That requires understanding some common algorithms for math-based movement and physics interactions. Simple movement based on points and vectors provides a foundation, and then it’s time to create objects that bounce off walls and one another with a bit of friction added to the mix. After that, we will step back and talk about movement that goes beyond straight lines: circles, spirals, and complex Bezier curves. We will then cover how adding gravity can affect movement. Finally, we will finish this chapter by discussing easing and how it can have a positive effect on math-based animations.

For the simplest kinds of animations—moving objects in a straight
line up and down the canvas—this can take the form of adding a constant
value to the `x`

or `y`

position of an object every time it is
drawn.

So, to animate graphics, we will need to create an interval and then
call a function that will display
our updated graphics on every frame. Each example in this chapter will be built in a similar way. The first
step is to set up the necessary variables in our `canvasApp()`

function. For this first, basic
example of movement, we will create a variable named `speed`

. We will apply this value to the `y`

position of our object on every call to
`drawScreen()`

. The `x`

and `y`

variables set up the initial position of the object (a filled circle) we
will move down the canvas:

var speed = 5; var y = 10; var x = 250;

After we create the variables, we set up an interval to call the
`drawScreen()`

function every 33
milliseconds. This is the loop we need to update our objects and move them
around the canvas:

setInterval(drawScreen, 33);

In the `drawScreen()`

function, we
update the value of `y`

by adding to it
the value of the `speed`

variable:

y += speed;

Finally, we draw our circle on the canvas. We position it using the
current value of `x`

and `y`

. Since `y`

is
updated every time the function is called, the circle effectively moves
down the canvas:

context.fillStyle = "#000000"; context.beginPath(); context.arc(x,y,15,0,Math.PI*2,true); context.closePath(); context.fill();

To move the circle up the screen, we would make `speed`

a negative number. To move it left or
right, we would update the `x`

instead of
the `y`

variable. To move the circle
diagonally, we would update both `x`

and
`y`

at the same time.

Example 5-1 shows the complete code needed to create basic movement in a straight line.

Example 5-1. Moving in a straight line

<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>CH5EX1: Moving In A Straight Line</title> <script src="modernizr-1.6.min.js"></script> <script type="text/javascript"> window.addEventListener('load', eventWindowLoaded, false); function eventWindowLoaded() { canvasApp(); } function canvasSupport () { return Modernizr.canvas; } function canvasApp() { if (!canvasSupport()) { return; } function drawScreen () { context.fillStyle = '#EEEEEE'; context.fillRect(0, 0, theCanvas.width, theCanvas.height); //Box context.strokeStyle = '#000000'; context.strokeRect(1, 1, theCanvas.width-2, theCanvas.height-2); // Create ball y += speed; context.fillStyle = "#000000"; context.beginPath(); context.arc(x,y,15,0,Math.PI*2,true); context.closePath(); context.fill(); } theCanvas = document.getElementById("canvasOne"); context = theCanvas.getContext("2d"); var speed = 5; var y = 10; var x = 250; setInterval(drawScreen, 33); } </script> </head> <body> <div style="position: absolute; top: 50px; left: 50px;"> <canvas id="canvasOne" width="500" height="500"> Your browser does not support HTML5 Canvas. </canvas> </div> </body> </html>

The basic structure of the HTML for all of the examples in this chapter will follow these rules. In the interest of saving space, we will refrain from discussing this code further, but it will appear in the examples provided.

Movement based on constant changes to the `x`

or `y`

position of an object works well for some applications, but other times
you will need to be more precise. One such instance is when you need to
move an object from point A to point B at a constant rate of
speed.

In mathematics, a common way to find the length of an unknown line is to use the Pythagorean theorem:

A^{2} + B^{2}
= C^{2} |

In this equation, C is the unknown side of a triangle when A and B are already known. However, we need to translate this equation into something we can use with the points and pixels we have available on the canvas.

This is a good example of using a mathematical equation in your application. In this case, we want to find the distance of a line, given two points. In English, this equation reads like this:

The distance equals the square root of the square of the difference between the

xvalue of the second point minus thexvalue of the first point, plus the square of the difference between theyvalue of the second point minus theyvalue of the first point.

You can see this in Equation 5-1. It’s much easier to understand in this format.

In the second example, we need to create some new variables in the
`canvasApp()`

function. We will still
use a `speed`

variable, just like in
the first example, but this time we set it to `5`

, which means it will move `5`

pixels on every call to `drawScreen()`

:

var speed = 5;

We then create a couple dynamic objects—each with an `x`

and a `y`

property—that will represent the two points we want to move between. For
this example, we will move our circle from `20`

,`250`

to
`480`

,`250`

:

var p1 = {x:20,y:250}; var p2 = {x:480,y:250};

Now it is time to re-create the distance equation in Equation 5-1. The first step is to calculate the
differences between the second and first `x`

and `y`

points:

var dx = p2.x - p1.x; var dy = p2.y - p1.y;

To determine the `distance`

, we
square both the values we just created, add them, and then use the
`Math.sqrt()`

function to get the
square root of the number:

var distance = Math.sqrt(dx*dx + dy*dy);

Next, we need to use that calculated `distance`

in a way that will allow us to move
an object a uniform number of pixels from `p1`

to `p2`

.
The first thing we do is calculate how many `moves`

(calls to `drawScreen()`

) it will take the object to move
at the given value of `speed`

. We get
this by dividing the `distance`

by the
`speed`

:

var moves = distance/speed;

Then we find the distance to move both `x`

and `y`

on
each call to `drawScreen()`

. We name
these variables `xunits`

and `yunits`

:

var xunits = (p2.x - p1.x)/moves; var yunits = (p2.y - p1.y)/moves;

Finally, we create a dynamic object named `ball`

that holds the `x`

and `y`

value of `p1`

…

var ball = {x:p1.x, y:p1.y};

…and create the interval to call `drawScreen()`

every 33 milliseconds:

setInterval(drawScreen, 33);

Let’s draw the ball on the screen. In the `drawScreen()`

function, we first check to see
whether the `moves`

variable is
greater than zero. If so, we are still supposed to move the ball
across the screen because we have not yet reached `p2`

. We decrement moves (`moves--`

) and then update the `x`

and `y`

properties of the ball object by adding the `xunits`

to `x`

and `yunits`

to `y`

:

if (moves > 0 ) { moves--; ball.x += xunits; ball.y += yunits; }

Now that our `values`

have been
updated, we simply draw the ball at the `x`

and `y`

coordinates specified by the `x`

and
`y`

properties, and we are done…that
is, until `drawScreen()`

is called 33
milliseconds later:

context.fillStyle = "#000000"; context.beginPath(); context.arc(ball.x,ball.y,15,0,Math.PI*2,true); context.closePath(); context.fill();

Let’s try the example by executing it in a web browser. You can
find it in the code distribution as *CH5EX2.html*, or you can type in Example 5-2. Watch the ball move from one
point to another. If you update the *x* and
*y* values of each point, or change the speed,
watch the results. You can do a lot with this very simple
example.

For many of the examples in this chapter, we will create a way to trace an object’s movement on the canvas by drawing points to show its path. We have done this to help illustrate how objects move. However, in the real world, you would need to remove this functionality so that your application would perform to its potential. This is the only place we will discuss this code, so if you see it listed in any of the later examples in this chapter, refer back to this section to refresh your memory on its functionality.

First, we create an array in `canvasApp()`

to hold the set of points we
will draw on the canvas:

var points = new Array();

Next, we load a black 4×4 pixel image, *point.png*, that we will use to display the
points on the canvas:

var pointImage = new Image(); pointImage.src = "point.png";

Whenever we calculate a point for an object we will move, we
push that point into the `points`

array:

points.push({x:ball.x,y:ball.y});

On each call to `drawScreen()`

,
we draw the set of points we have put into the `points`

array. Remember, we have to redraw
every point each time because the canvas is an immediate-mode display
surface that does not retain any information about the images drawn
onto it:

for (var i = 0; i< points.length; i++) { context.drawImage(pointImage, points[i].x, points[i].y,1,1); }

In Figure 5-1,
you can see what the ball looks like when moving on a line from one
point to another, and also what the `points`

path looks like when it is
drawn.

This is the only time in this chapter where we will discuss
the `points`

path in depth. If you
see the points being drawn, you will know how and why we have added
that functionality. You should also have enough information to
remove the code when necessary.

Example 5-2 is the full code
listing for *CH5EX2.html*.

Example 5-2. Moving on a simple line

<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>CH5EX2: Moving On A Simple Line</title> <script src="modernizr-1.6.min.js"></script> <script type="text/javascript"> window.addEventListener('load', eventWindowLoaded, false); function eventWindowLoaded() { canvasApp(); } function canvasSupport () { return Modernizr.canvas; } function canvasApp() { if (!canvasSupport()) { return; } var pointImage = new Image(); pointImage.src = "point.png"; function drawScreen () { context.fillStyle = '#EEEEEE'; context.fillRect(0, 0, theCanvas.width, theCanvas.height); //Box context.strokeStyle = '#000000'; context.strokeRect(1, 1, theCanvas.width-2, theCanvas.height-2); // Create ball if (moves > 0 ) { moves--; ball.x += xunits; ball.y += yunits; } //Draw points to illustrate path points.push({x:ball.x,y:ball.y}); for (var i = 0; i< points.length; i++) { context.drawImage(pointImage, points[i].x, points[i].y,1,1); } context.fillStyle = "#000000"; context.beginPath(); context.arc(ball.x,ball.y,15,0,Math.PI*2,true); context.closePath(); context.fill(); } var speed = 5; var p1 = {x:20,y:250}; var p2 = {x:480,y:250}; var dx = p2.x - p1.x; var dy = p2.y - p1.y; var distance = Math.sqrt(dx*dx + dy*dy); var moves = distance/speed; var xunits = (p2.x - p1.x)/moves; var yunits = (p2.y - p1.y)/moves; var ball = {x:p1.x, y:p1.y}; var points = new Array(); theCanvas = document.getElementById("canvasOne"); context = theCanvas.getContext("2d"); setInterval(drawScreen, 33); } </script> </head> <body> <div style="position: absolute; top: 50px; left: 50px;"> <canvas id="canvasOne" width="500" height="500"> Your browser does not support HTML5 Canvas. </canvas> </div> </body> </html>

Moving between two points is handy, but sometimes you don’t have a
point to move to, only a point to start from. In cases like this, it can
be very useful to create a `vector`

as
a means to move your object.

A *vector* is a quantity in physics that has
both magnitude and direction. For our purposes, the magnitude will be
the `speed`

of the moving object, and
the direction will be an `angle`

that
the object will move upon.

The good news is that moving on a vector is very similar to moving
between two points. In `canvasApp()`

,
we first set our `speed`

(magnitude).
This is the number of pixels the object will move on every call to
`drawScreen()`

. We will set this to
`5`

. We will also set the starting
point (`p1`

) for the object to `20`

,`20`

:

var speed = 5; var p1 = {x:20,y:20};

Now, we will set the `angle`

(direction) of movement for our object to `45`

degrees. In mathematics, a flat, straight
line usually represents the 0 angle, which means a vector with an angle
of 45 degrees would be down and to the right on the canvas.

With our angle set, we now need to convert it to radians. Radians are a standard unit of angle measurement, and most mathematical calculations require you to convert an angle into radians before you can use it.

So why not just use radians and forget degrees altogether? Because
it is much easier to understand movement in degrees when working with
vectors and moving objects on a 2D surface. While a circle has 360
degrees, it has just about 6 radians, which are calculated
counterclockwise. This might make perfect sense to mathematicians, but
to move objects on a computer screen, angles are much easier. So, we
will work with angles, but we still need to convert our 45-degree angle
into radians. We do that with a standard formula: `radians = angle * Math.PI/ 180`

. And in the
code:

var angle = 45; var radians = angle * Math.PI/ 180;

Before we can discuss how we calculate the movement of our object
along our vector, we need to review a couple trigonometric concepts.
These are `cosine`

and `sine`

, and both relate to the arc created by
our `angle`

(now converted to `radians`

), if it was drawn outward from the
center of the circle.

You can see how these values relate to a 45-degree angle in Figure 5-2.

This might seem complicated, but there is a very simple way to
think about it: cosine usually deals with the `x`

value, and sine usually deals with the
`y`

value. We can use sine and cosine
to help us calculate movement along our vector.

To calculate the number of pixels to move our object on each call
to `drawScreen()`

(`xunits`

and `yunits`

), we use the `radians`

(direction) we calculated and `speed`

(magnitude), along with the `Math.cos()`

(cosine) and `Math.sin()`

(sine) functions of the JavaScript
`Math`

object:

var xunits = Math.cos(radians) * speed; var yunits = Math.sin(radians) * speed;

In `drawScreen()`

, we simply add
`xunits`

and `yunits`

to `ball.x`

and `ball.y`

. We don’t check to see whether `moves`

has been exhausted because we are not
moving to a particular point—we are simply moving along the vector,
seemingly forever. In the next section, we will explore what we can do
if we want the moving object to change direction when it hits something
such as a wall:

ball.x += xunits; ball.y += yunits;

Figure 5-3 shows what Example 5-3 looks like when it is executed in a web browser. Recall that the points are drawn for illustration only.

Example 5-3 gives the full code listing.

Example 5-3. Moving on a vector

<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>CH5EX3: Moving On A Vector</title> <script src="modernizr-1.6.min.js"></script> <script type="text/javascript"> window.addEventListener('load', eventWindowLoaded, false); function eventWindowLoaded() { canvasApp(); } function canvasSupport () { return Modernizr.canvas; } function canvasApp() { if (!canvasSupport()) { return; } var pointImage = new Image(); pointImage.src = "point.png"; function drawScreen () { context.fillStyle = '#EEEEEE'; context.fillRect(0, 0, theCanvas.width, theCanvas.height); //Box context.strokeStyle = '#000000'; context.strokeRect(1, 1, theCanvas.width-2, theCanvas.height-2); ball.x += xunits; ball.y += yunits; //Draw points to illustrate path points.push({x:ball.x,y:ball.y}); for (var i = 0; i< points.length; i++) { context.drawImage(pointImage, points[i].x, points[i].y,1,1); } context.fillStyle = "#000000"; context.beginPath(); context.arc(ball.x,ball.y,15,0,Math.PI*2,true); context.closePath(); context.fill(); } var speed = 5; var p1 = {x:20,y:20}; var angle = 45; var radians = angle * Math.PI/ 180; var xunits = Math.cos(radians) * speed; var yunits = Math.sin(radians) * speed; var ball = {x:p1.x, y:p1.y}; var points = new Array(); theCanvas = document.getElementById("canvasOne"); context = theCanvas.getContext("2d"); setInterval(drawScreen, 33); } </script> </head> <body> <div style="position: absolute; top: 50px; left: 50px;"> <canvas id="canvasOne" width="500" height="500"> Your browser does not support HTML5 Canvas. </canvas> </div> </body> </html>

Start Free Trial

No credit card required