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

Simple Gravity, Elasticity, and Friction

Adding simulated gravity, elasticity, and friction to your objects adds a sense of realism that otherwise would not exist in 2D. These properties are major forces in nature that people feel and understand at nearly every moment of their lives. This means that people who play games expect objects to act in a particular way when these properties are applied. Our job is to simulate those effects as closely as possible, while minimizing the processing power necessary to create them. While there are some very complicated physics equations we could use to create these effects, we will use simplified versions that work well with the limited resources available to HTML5 Canvas in a web browser.

Simple Gravity

A very simple, yet seemingly realistic gravitational effect can be achieved by applying a constant gravity value to the y velocity of an object moving on a vector. To do this, select a value for gravity, such as .1, and then add that value to the y velocity of your object on every call to drawScreen().

For this example, let’s simulate a ball with a radius of 15 pixels being shot from a cannon that rests near the bottom of the canvas. The ball will move at a speed of 4 pixels per frame, with an angle of 305 degrees. This means it will move up and to the right on the canvas. If we did not apply any gravity, the ball would simply keep moving on that vector until it left the canvas (actually, it would keep moving, we just would not see it any longer).

You have seen the code to create an effect like this already. In the canvasApp() function, we would create the starting variables like this:

var speed = 4;
var angle = 305;
var radians = angle * Math.PI/ 180;
var radius = 15;
var vx = Math.cos(radians) * speed;
var vy = Math.sin(radians) * speed;

Next, we create the starting point for the ball as p1, and then create a dynamic object that holds all the values we created for the ball object:

var p1 = {x:20,y:theCanvas.width-radius};
var ball = {x:p1.x, y:p1.y, velocityx: vx, velocityy:vy, radius:radius};

If we want to add gravity to the application, we would first create a new variable named gravity and set it to a constant value of .1:

var gravity = .1;

Next, in the drawScreen() function, we apply this gravity value to the ball object when it is drawn to the canvas (ball.velocityy += gravity). We want the ball to stop moving when it hits the “ground” (the bottom of the canvas), so we test to see whether the y position of the ball plus the radius of the ball (the outer edge) has passed the bottom of the canvas (ball.y + ball.radius <= theCanvas.height). If so, we stop the ball’s movement:

if (ball.y + ball.radius <= theCanvas.height) {
   ball.velocityy += gravity;
} else {
   ball.velocityx = 0;
   ball.velocityy = 0;
   ball.y = theCanvas.height - ball.radius;

}

Next, we apply the constant x velocity and the new y velocity to ball, and draw it to the canvas:

ball.y += ball.velocityy;
ball.x += ball.velocityx;

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

Figure 5-17 shows what the path looks like when simple gravity is applied to a ball moving on a vector. We have added the points to illustrate the path.

Simple gravity with an object moving on a vector

Figure 5-17. Simple gravity with an object moving on a vector

You can test out Example 5-14 with the file CH5EX14.html in the code distribution, or type in the full code listing below.

Example 5-14. Simple gravity

a<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CH5EX14: Simple Gravity</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);

      if (ball.y + ball.radius <= theCanvas.height) {
         ball.velocityy += gravity;
      } else {
         ball.velocityx = 0;
         ball.velocityy = 0;
         ball.y = theCanvas.height - ball.radius;

      }

      ball.y += ball.velocityy;
      ball.x += ball.velocityx;

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

   }
   var speed = 4;

   var gravity = .1;
   var angle = 305;
   var radians = angle * Math.PI/ 180;
   var radius = 15;
   var vx = Math.cos(radians) * speed;
   var vy = Math.sin(radians) * speed;

   theCanvas = document.getElementById("canvasOne");
   context = theCanvas.getContext("2d");

   var p1 = {x:20,y:theCanvas.width-radius};
   var ball = {x:p1.x, y:p1.y, velocityx: vx, velocityy:vy, radius:radius};

   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>

Simple Gravity with a Bounce

The last example showed what a cannonball might look like if it was shot out, landed on a surface, and stuck there with no reaction. However, even a heavy cannonball will bounce when it hits the ground.

To create a bouncing effect we do not have to change the code very much at all. In drawScreen(), we first apply gravity on every frame; then, instead of stopping the ball if it hits the bottom of the canvas, we simply need to reverse the y velocity of ball when it hits the ground.

In CH5EX14.html you would replace this code…

if (ball.y + ball.radius <= theCanvas.height) {
   ball.velocityy += gravity;
} else {
   ball.velocityx = 0;
   ball.velocityy = 0;
   ball.y = theCanvas.height - ball.radius;
}

…with this:

ball.velocityy += gravity;
if ((ball.y + ball.radius) > theCanvas.height) {
   ball.velocityy = -(ball.velocityy)
}

This code will send the ball bouncing back “up” the canvas. Since it is still traveling on the vector, and gravity is applied every time drawScreen() is called, the ball will eventually come down again as the applied gravity overtakes the reversed y velocity.

Figure 5-18 shows what the cannonball looks like when the bounce is applied.

A ball moving on a vector with gravity and a bounce applied

Figure 5-18. A ball moving on a vector with gravity and a bounce applied

Note

To achieve a nice-looking bounce for this example, we also changed the angle of the vector in canvasApp() to 295:

var angle = 295;

Example 5-15 offers the full code.

Example 5-15. Simple gravity with a bounce

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CH5EX15: Gravity With A Bounce</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.velocityy  += gravity;

      if ((ball.y + ball.radius) > theCanvas.height) {
         ball.velocityy = -(ball.velocityy)
      }
      ball.y += ball.velocityy;
      ball.x += ball.velocityx;

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

   }
   var speed = 5;

   var gravity = .1;
   var angle = 295;
   var radians = angle * Math.PI/ 180;
   var radius = 15;

   var vx = Math.cos(radians) * speed;
   var vy = Math.sin(radians) * speed;

   theCanvas = document.getElementById("canvasOne");
   context = theCanvas.getContext("2d");

   var p1 = {x:20,y:theCanvas.width-radius};
   var ball = {x:p1.x, y:p1.y, velocityx: vx, velocityy:vy, radius:radius};

   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>

Gravity with Bounce and Applied Simple Elasticity

In physics, the elasticity of a bouncing ball refers to how much energy is conserved when a ball bounces off a surface. We already covered a bit about conservation of energy when we discussed balls colliding, but when we are simulating objects falling, we need to take a slightly different path with our code. In Example 5-15, we applied 100% elasticity and the ball bounced forever (actually, this was only implied since we did not consider elasticity at all). However, in real life, balls usually lose some of their energy every time they bounce off a surface. The amount of energy conserved depends on the material the ball is made from, as well as the surface it is bouncing on. For example, a rubber Super Ball is much more elastic than a cannonball and will bounce higher on the first bounce off a surface. Both will bounce higher off a concrete surface than a surface made of thick mud. Eventually, both will come to rest on the surface as all the energy is transferred away from the ball.

We can simulate simple elasticity by applying a constant value to the ball when it bounces off the ground. For this example, we will set the speed of the ball to 6 pixels per frame, and the angle to 285. We will keep our gravity at .1, but set a new variable named elasticity to .5. To make this more straightforward, we will also assume that the surface the ball is bouncing on does not add or subtract from the elasticity of the ball.

In canvasApp() we would set the new properties like this:

var speed = 6;
var gravity = .1;
var elasticity = .5;
var angle = 285;

We then add the new elasticity property to the ball object because, unlike gravity, elasticity describes a property of an object, not the entire world it resides within. This means that having multiple balls with different values for elasticity would be very easy to implement:

var ball = {x:p1.x, y:p1.y, velocityx: vx, velocityy:vy, radius:radius, 
    elasticity: elasticity};

In the drawScreen() function, we still add the gravity value to the y velocity (velocityy). However, instead of simply reversing the y velocity when the ball hits the bottom of the canvas, we also multiply the y velocity by the elasticity value stored in the ball.elasticity property. This applies the elasticity to the bounce, preserving the y velocity by the percentage value of elasticity for the object:

ball.velocityy += gravity;
if ((ball.y + ball.radius) > theCanvas.height) {
   ball.velocityy = -(ball.velocityy)*ball.elasticity;
}
ball.y += ball.velocityy;
ball.x += ball.velocityx;

In Figure 5-19 you can see what this application looks like when executed in a web browser.

Ball bouncing with elasticity and gravity applied

Figure 5-19. Ball bouncing with elasticity and gravity applied

Note

With gravity applied, the bounce is not exactly as you might expect. Gravity is always pulling down on our object, so the effect of a loss of y velocity due to an elastic bounce is pronounced.

The full code is shown in Example 5-16.

Example 5-16. Simple gravity with bounce and elasticity

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CH5EX16: Gravity With A Vector With Bounce And Elasticity</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.velocityy += gravity;

      if ((ball.y + ball.radius) > theCanvas.height) {
         ball.velocityy = -(ball.velocityy)*ball.elasticity;
      }
      ball.y += ball.velocityy;
      ball.x += ball.velocityx;

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

   }
   var speed = 6;
   var gravity = .1;
   var elasticity = .5;
   var angle = 285;
   var radians = angle * Math.PI/ 180;
   var radius = 15;

   var vx = Math.cos(radians) * speed;
   var vy = Math.sin(radians) * speed;

   theCanvas = document.getElementById("canvasOne");
   context = theCanvas.getContext("2d");

   var p1 = {x:20,y:theCanvas.width-radius};
   var ball = {x:p1.x, y:p1.y, velocityx: vx, velocityy:vy, radius:radius, 
       elasticity: elasticity};

   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>

Simple Gravity, Simple Elasticity, and Simple Friction

Now that we have a ball traveling on a vector that is affected by both gravity and elasticity, we have one more element to add to make the animation more realistic. In the previous example, the y velocity was affected by gravity and elasticity, but the ball still traveled on the x-axis without any degradation in velocity. We will fix this issue now by adding friction into the equation.

In physics, friction is a force that resists the motion of an object. We have already discussed friction as it applies to colliding balls, and this implementation is similar except that it affects only the x velocity. For our purposes, we will achieve simple friction by degrading the x velocity as gravity degrades the y velocity.

Taking the code from Example 5-16, in canvasApp() we create a new variable named friction. This is the amount of pixels to degrade the x velocity on every frame:

var friction = .008;

Notice that the amount is quite small. Friction does not have to be a large value to look realistic—it just needs to be applied uniformly each time drawScreen() is called. In drawScreen(), we apply friction to the x velocity like this:

ball.velocityx = ball.velocityx - ( ball.velocityx*friction);

This is the same type of proportional application of friction we used with the colliding balls, but again, this time we applied it only to the x velocity.

Figure 5-20 shows what this final version of our application looks like when executed in a web browser.

Ball bouncing with gravity, elasticity, and friction applied

Figure 5-20. Ball bouncing with gravity, elasticity, and friction applied

Example 5-17 gives the full code for CH5EX17.html, the final code of our simple gravity, simple elasticity, and simple friction example.

Example 5-17. Gravity with a vector with bounce friction

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CH5EX17: Gravity With A Vector With Bounce Friction</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.velocityx = ball.velocityx - ( ball.velocityx*friction);

      ball.velocityy += gravity;

      if ((ball.y + ball.radius) > theCanvas.height) {
         ball.velocityy = -(ball.velocityy)*ball.elasticity;
      }
      ball.y += ball.velocityy;
      ball.x += ball.velocityx;

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

   }
   var speed = 6;
   var gravity = .1;
   var friction = .008;
   var elasticity = .5;
   var angle = 285;
   var radians = angle * Math.PI/ 180;
   var radius = 15;

   var vx = Math.cos(radians) * speed;
   var vy = Math.sin(radians) * speed;

   theCanvas = document.getElementById("canvasOne");
   context = theCanvas.getContext("2d");

   var p1 = {x:20,y:theCanvas.width-radius};
   var ball = {x:p1.x, y:p1.y, velocityx: vx, velocityy:vy, radius:radius, 
       elasticity: elasticity};

   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.