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

Curve and Circular Movement

Whew! Moving and colliding balls on vectors can create some cool effects. However, moving in straight lines is not the only way you might want to move objects. In this section, we will show you some ways to animate objects using circles, spirals, and curves.

Uniform Circular Motion

Uniform circular motion occurs when we move an object along the distinct radius of a defined circle. Once we know the radius, we can use our old friends cosine and sine to find the x and y locations of the moving object. The equations to find the locations of an object moving uniformly on a defined circle are:

x = radius * cosine(angle)
y = radius * sine(angle)

We will create an example of uniform circular movement with a circle that has a radius of 125, with its center position at 250,250 on the canvas. We will move a ball along that circle, starting at an angle of 0.

In canvasApp(), we will define this circle path as a dynamic object stored in the circle variable. While this object defines the properties of a circle, we will not actually draw this circle on the canvas; rather, it defines only the path on which we will move our ball object:

var circle = {centerX:250, centerY:250, radius:125, angle:0}
var ball = {x:0, y:0,speed:.1};

In drawScreen(), we will incorporate the equations for uniform circular movement. To do this, we will set the x and y properties of the ball object to the products of the equations, added to the center location of the circle path on the canvas (circle.centerX, circle.centerY):

ball.x = circle.centerX + Math.cos(circle.angle) * circle.radius;
ball.y = circle.centerY + Math.sin(circle.angle) * circle.radius;

We then add the speed of the ball to the angle of the circle path. This effectively sets the ball to move to a new location the next time drawScreen() is called:

circle.angle += ball.speed;

Finally, we draw the ball onto the canvas:

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

You can see what the circle path looks like in Figure 5-12. We have drawn the points on the canvas to illustrate the circle path.

Moving an object in a circle

Figure 5-12. Moving an object in a circle

You can easily alter the location and size of the circle path by altering the radius, centerX, and centerY properties of the circle path object.

Example 5-9 shows the code for CH5EX9.html.

Example 5-9. Moving in a circle

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CH5EX9: Moving In A Circle</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);

      ball.x = circle.centerX + Math.cos(circle.angle) * circle.radius;
      ball.y = circle.centerY + Math.sin(circle.angle) * circle.radius;

      circle.angle += ball.speed;

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

   }

   var radius = 100;
   var circle = {centerX:250, centerY:250, radius:125, angle:0}
   var ball = {x:0, y:0,speed:.1};

   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 in a Simple Spiral

There are many complicated ways to move an object on a spiral path. One such way would be to use the Fibonacci sequence, which describes a pattern seen in nature that appears to create perfect spirals. The Fibonacci sequence starts with the number 0, and continues with each subsequent number calculated as the sum of the two previous numbers in the sequence. Each subsequent rotation of the spiral is the sum of the two previous numbers (1, 2, 3, 5, 8, 13, 21, 34, 55, 89...). However, as you might imagine, the math used to create this sequence is quite involved, and it is also difficult to translate to object movement.

For our purposes, we can create a simple spiral by increasing the radius of the circle path on each call to drawScreen(). If we take the code from Example 5-9, we would add a radiusInc variable, which we will use as the value to add the radius movement path of the circle. We create this new variable in canvasApp():

var radiusInc = 2;

Then, in drawScreen(), we add the following code to increase the radius of the circle every time we move the object:

circle.radius += radiusInc;

In Figure 5-13, you can see what the resulting spiral looks like (to illustrate the path, this example includes the points).

If you want a tighter spiral, decrease the value of radiusInc. Conversely, if you want a wider spiral, increase the value of radiusInc.

Example 5-10 shows the code for CH5EX10.html from the code distribution.

Moving an object in a simple spiral pattern

Figure 5-13. Moving an object in a simple spiral pattern

Example 5-10. Moving in a simple geometric spiral

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CH5EX10: Moving In A Simple Geometric Spiral </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 = circle.centerX + Math.cos(circle.angle) * circle.radius;
      ball.y = circle.centerY + Math.sin(circle.angle) * circle.radius;
      circle.angle += ball.speed;
      circle.radius += radiusInc;

      //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 radiusInc = 2;
   var circle = {centerX:250, centerY:250, radius:2, angle:0, radiusInc:2}
   var ball = {x:0, y:0,speed:.1};
   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>

Cubic Bezier Curve Movement

Cubic Bezier curves can be used to define a movement path for an object. Pierre Bezier first popularized these curves in the 1960s. They are widely used in 2D vector graphics to define smooth curves for drawing, but they can also be used in animation to define a path for motion.

A cubic Bezier curve is created using four distinct points—p0, p1, p2, and p3:

p0

The starting point of the curve. We will refer to these x and y values as x0 and y0.

p3

The ending point of the curve. We will refer to these x and y values as x3 and y3.

p1 and p2

The control points for the curve. The curve does not pass through these points; instead, the equation uses these points to determine the arc of the curve. We will refer to these x and y values as x0, x1, x2, x3, y0, y1, y2, and y3.

Note

The usage of the p1 and p2 points is the biggest stumbling block for understanding Bezier curves. The easiest way to understand the relationship between these points and the curve is to draw them on a bitmapped canvas, which we will do several times in this chapter.

After you have the four points, you need to calculate six coefficient values that you will use to find the x and y locations as you move an object on the curve. These coefficients are known as ax, bx, cx, ay, by, and cy. They are calculated as follows:

cx = 3 (x1 - x0)
bx = 3 (x2 - x1) - cx
ax = x3 - x0 - cx - bx
cy = 3 (y1 - y0)
by = 3 (y2 - y1) - cy
ay = y3 - y0 - cy - by

After you’ve calculated the six coefficients, you can find the x and y locations based on the changing t value using the following equations. The t value represents movement over time:

x(t) = axt3 + bxt2 + cxt + x0
y(t) = ayt3 + byt2 + cyt + y0

For our purposes, the t value will be increased by the speed at which we want the object to move. You will notice, though, that this value does not easily equate to the speed values we used elsewhere in this chapter. The reason is that the t value was not created with movement over time for animation in mind. The speed we specify must be smaller than 1 so the movement on the curve will be incremental enough for us to see it as part of the animation. For our example, we will increase t by a speed of .01, so that we will see 100 points on the movement curve (1/100 = .01). This is advantageous because we will know our object has finished moving when the t value is equal to 1.

For Example 5-11 (CH5EX11.html), we will start by creating the four points of the Bezier curve in the canvasApp() function:

var p0 = {x:60, y:10};
var p1 = {x:70, y:200};
var p2 = {x:125, y:295};
var p3 = {x:350, y:350};

We then create a new ball object with a couple differing properties from those in the other examples in this chapter. The speed is .01, which means the object will move 100 points along the curve before it is finished. We start the t value at 0, which means the ball will begin at p0:

var ball = {x:0, y:0, speed:.01, t:0};

Next, in the drawScreen() function, we calculate the Bezier curve coefficient values (ax, bx, cx, ay, by, cy) based on the four points (p0, p1, p2, p3):

var cx = 3 * (p1.x - p0.x)
var bx = 3 * (p2.x - p1.x) - cx;
var ax = p3.x - p0.x - cx - bx;

var cy = 3 * (p1.y - p0.y);
var by = 3 * (p2.y - p1.y) - cy;
var ay = p3.y - p0.y - cy - by;

Then, we take our t value and use it with the coefficients to calculate the x and y values for the moving object. First, we get the t value from the ball object, and store it locally so we can use it in our calculations:

var t = ball.t;

Next, we add the speed to the t value so that we can calculate the next point on the Bezier path:

ball.t += ball.speed;

Then, we use the t value to calculate the x and y values (xt, yt) using the Bezier curve equations:

var xt = ax*(t*t*t) + bx*(t*t) + cx*t + p0.x;
var yt = ay*(t*t*t) + by*(t*t) + cy*t + p0.y;

We add the speed to the t value of ball, then check to see whether t is greater than 1. If so, we don’t increase it any further because we have finished moving on the curve:

ball.t += ball.speed;

if (ball.t > 1) {
   ball.t = 1;
}

Finally, when we draw the ball object on the canvas, we use the xt and yt values:

context.arc(xt,yt,5,0,Math.PI*2,true);

Figure 5-14 shows what Example 5-11 (CH5EX11.html) looks like when it is executed in a web browser. In addition to drawing the points of the path using the points array, we also draw the four points of the Bezier curve. These illustrate the relationship of the points to the curve itself. Notice that the curve does not pass through p1 or p2.

Moving a circle on a Bezier curve

Figure 5-14. Moving a circle on a Bezier curve

Example 5-11 gives the full code listing for CH5EX11.html, including the code to draw the Bezier curve points on the canvas. You can find that code in the drawScreen() function following the //draw the points comment.

Example 5-11. Moving on a cubic Bezier curve

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CH5EX11: Moving On A Cubic Bezier Curve </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);

      var t = ball.t;

      var cx = 3 * (p1.x - p0.x)
      var bx = 3 * (p2.x - p1.x) - cx;
      var ax = p3.x - p0.x - cx - bx;

      var cy = 3 * (p1.y - p0.y);
      var by = 3 * (p2.y - p1.y) - cy;
      var ay = p3.y - p0.y - cy - by;

      var xt = ax*(t*t*t) + bx*(t*t) + cx*t + p0.x;
      var yt = ay*(t*t*t) + by*(t*t) + cy*t + p0.y;

      ball.t += ball.speed;

      if (ball.t > 1) {
         ball.t = 1;
      }

      //draw the points

      context.font ="10px sans";
      context.fillStyle = "#FF0000";
      context.beginPath();
      context.arc(p0.x,p0.y,8,0,Math.PI*2,true);
      context.closePath();
      context.fill();
      context.fillStyle = "#FFFFFF";
      context.fillText("0",p0.x-2,p0.y+2);

      context.fillStyle = "#FF0000";
      context.beginPath();
      context.arc(p1.x,p1.y,8,0,Math.PI*2,true);
      context.closePath();
      context.fill();
      context.fillStyle = "#FFFFFF";
      context.fillText("1",p1.x-2,p1.y+2);

      context.fillStyle = "#FF0000";
      context.beginPath();
      context.arc(p2.x,p2.y,8,0,Math.PI*2,true);
      context.closePath();
      context.fill();
      context.fillStyle = "#FFFFFF";
      context.fillText("2",p2.x-2, p2.y+2);

      context.fillStyle = "#FF0000";
      context.beginPath();
      context.arc(p3.x,p3.y,8,0,Math.PI*2,true);
      context.closePath();
      context.fill();
      context.fillStyle = "#FFFFFF";
      context.fillText("3",p3.x-2, p3.y+2);

      //Draw points to illustrate path

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

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

      context.closePath();

      //Draw circle moving

      context.fillStyle = "#000000";
      context.beginPath();
      context.arc(xt,yt,5,0,Math.PI*2,true);
      context.closePath();
      context.fill();

   }

   var p0 = {x:60, y:10};
   var p1 = {x:70, y:200};
   var p2 = {x:125, y:295};
   var p3 = {x:350, y:350};
   var ball = {x:0, y:0, speed:.01, t:0};
   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 an Image

Moving an image on a cubic Bezier curve path is just as easy as moving a circular drawing object, as we’ll demonstrate in the next two examples. Suppose you are making a game where bullseyes move across the canvas and the player must shoot at them. You could use cubic Bezier curve paths to create new and interesting patterns for the bullseyes to move along.

For this example, we first create a global variable named bullseye, which we will use to hold the bullseye.png image that we will load to display on the canvas:

var bullseye;
function eventWindowLoaded() {
   bullseye = new Image();
   bullseye.src = "bullseye.png"
   bullseye.onload = eventAssetsLoaded;
}

In canvasApp(), we will create a different path for the curve from the one in the first example by setting new values for p0, p1, p2, and p3. Using these values, the bullseye will move on a parabola-like path (Figure 5-15 shows the path of the curve):

var p0 = {x:60, y:10};
var p1 = {x:150, y:350};
var p2 = {x:300, y:375};
var p3 = {x:400, y:20};

We also need to create a player object that represents the bullseye on the canvas:

var player = {x:0, y:0, speed:.01, t:0};

In drawImage(), after we calculate t, xt, and yt, we draw the image on the canvas:

player.x = xt-bullseye.width/2;
      player.y = yt-bullseye.height/2;

      context.drawImage(bullseye,player.x,player.y);
Moving an image on a cubic Bezier curve path

Figure 5-15. Moving an image on a cubic Bezier curve path

The rest of Example 5-12 works just like Example 5-11.

Example 5-12. Moving an image

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CH5EX12: Moving An Image </title>
<script src="modernizr-1.6.min.js"></script>
<script type="text/javascript">
window.addEventListener('load', eventWindowLoaded, false);

var bullseye;
function eventWindowLoaded() {
   bullseye = new Image();
   bullseye.src = "bullseye.png"
   bullseye.onload = eventAssetsLoaded;
}

function eventAssetsLoaded() {

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

      var t = player.t;

      var cx = 3 * (p1.x - p0.x)
      var bx = 3 * (p2.x - p1.x) - cx;
      var ax = p3.x - p0.x - cx - bx;

      var cy = 3 * (p1.y - p0.y);
      var by = 3 * (p2.y - p1.y) - cy;
      var ay = p3.y - p0.y - cy - by;

      var xt = ax*(t*t*t) + bx*(t*t) + cx*t + p0.x;

      var yt = ay*(t*t*t) + by*(t*t) + cy*t + p0.y;

      player.t += player.speed;

      if (player.t > 1) {
         player.t = 1;
      }      //draw the points

      context.font = "10px sans";

      context.fillStyle = "#FF0000";
      context.beginPath();
      context.arc(p0.x,p0.y,8,0,Math.PI*2,true);
      context.closePath();
      context.fill();
      context.fillStyle = "#FFFFFF";
      context.fillText("0",p0.x-2,p0.y+2);

      context.fillStyle = "#FF0000";
      context.beginPath();
      context.arc(p1.x,p1.y,8,0,Math.PI*2,true);
      context.closePath();
      context.fill();
      context.fillStyle = "#FFFFFF";
      context.fillText("1",p1.x-2,p1.y+2);

      context.fillStyle = "#FF0000";
      context.beginPath();
      context.arc(p2.x,p2.y,8,0,Math.PI*2,true);
      context.closePath();
      context.fill();
      context.fillStyle = "#FFFFFF";
      context.fillText("2",p2.x-2, p2.y+2);

      context.fillStyle = "#FF0000";
      context.beginPath();
      context.arc(p3.x,p3.y,8,0,Math.PI*2,true);
      context.closePath();
      context.fill();
      context.fillStyle = "#FFFFFF";
      context.fillText("3",p3.x-2, p3.y+2);

      //Draw points to illustrate path

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

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

      context.closePath();

      player.x = xt-bullseye.width/2;
      player.y = yt-bullseye.height/2;

      context.drawImage(bullseye,player.x,player.y);

   }

   var p0 = {x:60, y:10};
   var p1 = {x:150, y:350};
   var p2 = {x:300, y:375};
   var p3 = {x:400, y:20};
   var player = {x:0, y:0, speed:.01, t:0};
   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>

Creating a Cubic Bezier Curve Loop

You can create some very interesting paths using the four points in a cubic Bezier curve. One such effect is a loop. To create a loop, you need to make sure the points form an X, with p0 diagonal from p1, and p2 and p3 on an opposite diagonal from the other two points. p0 and p3 must be closer to the center of the canvas than p1 or p2. Below are the points we will use to create this effect in Example 5-13:

var p0 = {x:150, y:440;
var p1 = {x:450, y:10};
var p2 = {x:50, y:10};
var p3 = {x:325, y:450};

Since it is much easier to show than tell when it comes to cubic Bezier curves, look at Figure 5-16. It shows what the looping curve looks like when Example 5-13 is executed in a web browser.

Note

This effect can only be created with the four points of a cubic Bezier curve. There is also a three-point Bezier curve known as a quadratic Bezier curve. You cannot create loops or S curves with quadratic Bezier curves because the three points are not as precise as the four points of a cubic Bezier curve.

Moving an object in a loop using a cubic Bezier curve

Figure 5-16. Moving an object in a loop using a cubic Bezier curve

Since the code for this example is essentially the same as in Example 5-12 (besides the four points), we have highlighted in bold the changed code in Example 5-13. We have done this to show you that—with relatively simple changes—you can create dramatic animation effects using cubic Bezier curves.

Example 5-13. Bezier curve loop

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CH5EX13: Bezier Curve Loop </title>
<script src="modernizr-1.6.min.js"></script>
<script type="text/javascript">
window.addEventListener('load', eventWindowLoaded, false);

var bullseye;
function eventWindowLoaded() {
   bullseye = new Image();
   bullseye.src = "bullseye.png"
   bullseye.onload = eventAssetsLoaded;
}

function eventAssetsLoaded() {

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

      var t = player.t;

      var cx = 3 * (p1.x - p0.x)
      var bx = 3 * (p2.x - p1.x) - cx;
      var ax = p3.x - p0.x - cx - bx;

      var cy = 3 * (p1.y - p0.y);
      var by = 3 * (p2.y - p1.y) - cy;
      var ay = p3.y - p0.y - cy - by;

      var xt = ax*(t*t*t) + bx*(t*t) + cx*t + p0.x;

      var yt = ay*(t*t*t) + by*(t*t) + cy*t + p0.y;

      player.t += player.speed;

      if (player.t > 1) {
         player.t = 1;
      }
      //draw the points

      context.font = "10px sans";

      context.fillStyle = "#FF0000";
      context.beginPath();
      context.arc(p0.x,p0.y,8,0,Math.PI*2,true);
      context.closePath();
      context.fill();
      context.fillStyle = "#FFFFFF";
      context.fillText("0",p0.x-2,p0.y+2);

      context.fillStyle = "#FF0000";
      context.beginPath();
      context.arc(p1.x,p1.y,8,0,Math.PI*2,true);
      context.closePath();
      context.fill();
      context.fillStyle = "#FFFFFF";
      context.fillText("1",p1.x-2,p1.y+2);

      context.fillStyle = "#FF0000";
      context.beginPath();
      context.arc(p2.x,p2.y,8,0,Math.PI*2,true);
      context.closePath();
      context.fill();
      context.fillStyle = "#FFFFFF";
      context.fillText("2",p2.x-2, p2.y+2);

      context.fillStyle = "#FF0000";
      context.beginPath();
      context.arc(p3.x,p3.y,8,0,Math.PI*2,true);
      context.closePath();
      context.fill();
      context.fillStyle = "#FFFFFF";
      context.fillText("3",p3.x-2, p3.y+2);

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

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

      context.closePath();

      player.x = xt-bullseye.width/2;
      player.y = yt-bullseye.height/2;

      context.drawImage(bullseye,player.x,player.y);

   }

   var p0 = {x:150, y:440};
   var p1 = {x:450, y:10};
   var p2 = {x:50, y:10};
   var p3 = {x:325, y:450};
   var player = {x:0, y:0, speed:.01, t:0};

   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.