You are previewing HTML5 Canvas.

HTML5 Canvas

Cover of HTML5 Canvas by Steve Fulton... Published by O'Reilly Media, Inc.
  1. HTML5 Canvas
    1. SPECIAL OFFER: Upgrade this ebook with O’Reilly
    2. A Note Regarding Supplemental Files
    3. Preface
      1. Running the Examples in the Book
      2. What You Need to Know
      3. How This Book Is Organized
      4. Conventions Used in This Book
      5. Using Code Examples
      6. We’d Like to Hear from You
      7. Safari® Books Online
      8. Acknowledgments
    4. 1. Introduction to HTML5 Canvas
      1. The Basic HTML Page
      2. Basic HTML We Will Use in This Book
      3. The Document Object Model (DOM) and Canvas
      4. JavaScript and Canvas
      5. HTML5 Canvas “Hello World!”
      6. Debugging with Console.log
      7. The 2D Context and the Current State
      8. The HTML5 Canvas Object
      9. Another Example: Guess The Letter
      10. What’s Next
    5. 2. Drawing on the Canvas
      1. The Basic File Setup for This Chapter
      2. The Basic Rectangle Shape
      3. The Canvas State
      4. Using Paths to Create Lines
      5. Advanced Path Methods
      6. Compositing on the Canvas
      7. Simple Canvas Transformations
      8. Filling Objects with Colors and Gradients
      9. Filling Shapes with Patterns
      10. Creating Shadows on Canvas Shapes
      11. What’s Next
    6. 3. The HTML5 Canvas Text API
      1. Displaying Basic Text
      2. Setting the Text Font
      3. Text and the Canvas Context
      4. Text with Gradients and Patterns
      5. Width, Height, Scale, and toDataURL() Revisited
      6. Final Version of Text Arranger
      7. What’s Next
    7. 4. Images on the Canvas
      1. The Basic File Setup for This Chapter
      2. Image Basics
      3. Simple Cell-Based Sprite Animation
      4. Advanced Cell-Based Animation
      5. Applying Rotation Transformations to an Image
      6. Creating a Grid of Tiles
      7. Zooming and Panning an Image
      8. Pixel Manipulation
      9. Copying from One Canvas to Another
      10. What’s Next
    8. 5. Math, Physics, and Animation
      1. Moving in a Straight Line
      2. Bouncing Off Walls
      3. Curve and Circular Movement
      4. Simple Gravity, Elasticity, and Friction
      5. Easing
      6. What’s Next?
    9. 6. Mixing HTML5 Video and Canvas
      1. HTML5 Video Support
      2. Converting Video Formats
      3. Basic HTML5 Video Implementation
      4. Preloading Video in JavaScript
      5. Video and the Canvas
      6. Video on the Canvas Examples
      7. Animation Revisited: Moving Videos
      8. What’s Next?
    10. 7. Working with Audio
      1. The Basic <audio> Tag
      2. Audio Formats
      3. Audio Tag Properties, Functions, and Events
      4. Playing a Sound with No Audio Tag
      5. Creating a Canvas Audio Player
      6. Case Study in Audio: Space Raiders Game
      7. What’s Next
    11. 8. Canvas Game Essentials
      1. Why Games in HTML5?
      2. Our Basic Game HTML5 File
      3. Our Game’s Design
      4. Game Graphics: Drawing with Paths
      5. Animating on the Canvas
      6. Applying Transformations to Game Graphics
      7. Game Graphic Transformations
      8. Game Object Physics and Animation
      9. A Basic Game Framework
      10. Putting It All Together
      11. The player Object
      12. Geo Blaster Game Algorithms
      13. The Geo Blaster Basic Full Source
      14. Rock Object Prototype
      15. What’s Next
    12. 9. Combining Bitmaps and Sound
      1. Geo Blaster Extended
      2. Creating a Dynamic Tile Sheet at Runtime
      3. A Simple Tile-Based Game
      4. What’s Next
    13. 10. Mobilizing Games with PhoneGap
      1. Going Mobile!
      2. Creating the iOS Application with PhoneGap
      3. Beyond the Canvas
      4. What’s Next
    14. 11. Further Explorations
      1. 3D with WebGL
      2. Multiplayer Applications with ElectroServer 5
      3. Conclusion
    15. Index
    16. About the Authors
    17. Colophon
    18. SPECIAL OFFER: Upgrade this ebook with O’Reilly
O'Reilly logo

Chapter 5. Math, Physics, and Animation

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.

Moving in a Straight Line

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>

Note

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.

Moving Between Two Points: The Distance of a Line

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:

A2 + B2 = C2

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 x value of the second point minus the x value of the first point, plus the square of the difference between the y value of the second point minus the y value of the first point.

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

Equation 5-1. Distance equation

Distance equation

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);

Drawing the ball

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.

Tracing movement: A path of points

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.

Note

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.

A ball moving from one point to another along the line, with the points drawn for illustration

Figure 5-1. A ball moving from one point to another along the line, with the points drawn for illustration

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 on a Vector

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.

cosine

The angle measured counterclockwise from the x-axis (x)

sine

The vertical coordinate of the arc endpoint (y)

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

Angles on the canvas

Figure 5-2. Angles on the canvas

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.

Moving an object on a vector

Figure 5-3. Moving an object on a vector

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>

The best content for your career. Discover unlimited learning on demand for around $1/day.