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 Canvas Transformations

Transformations on the canvas refer to the mathematical adjustment of physical properties of drawn shapes. The two most commonly used shape transformations are scale and rotate, which we will focus on in this section.

Under the hood, a mathematical matrix operation applies to all transformations. Luckily, you do not need to understand this to use simple Canvas transformations. We will discuss how to apply rotation, translation, and scale transformations by changing simple Canvas properties.

Rotation and Translation Transformations

An object on the canvas is said to be at the 0 angle rotation when it is facing to the left (this is important if an object has a facing; otherwise, we will use this as a guide). Consequently, if we draw an equilateral box (all four sides are the same length), it doesn’t have an initial facing other than one of the flat sides facing to the left. Let’s draw that box for reference:

//now draw a red square
context.fillStyle = "red";
context.fillRect(100,100,50,50);

Now, if we want to rotate the entire canvas 45 degrees, we need to do a couple simple steps. First, we always set the current Canvas transformation to the “identity” (or “reset”) matrix:

context.setTransform(1,0,0,1,0,0);

Because Canvas uses radians, not degrees, to specify its transformations, we need to convert our 45-degree angle into radians:

var angleInRadians = 45 * Math.PI / 180;
context.rotate(angleInRadians);

Lesson 1: Transformations are applied to shapes and paths drawn after the setTransform() or other transformation function is called

If you use this code verbatim, you will see a funny result…nothing! This is because the setTransform() function call only affects shapes drawn to the canvas after it is applied. We drew our square first, then set the transformation properties. This resulted in no change (or transform) to the drawn square. Example 2-7 gives the code in the correct order to produce the expected result, as illustrated in Figure 2-12.

Example 2-7. Simple rotation transformation

function drawScreen() {

      //now draw a red square
      context.setTransform(1,0,0,1,0,0);
      var angleInRadians = 45 * Math.PI / 180;
      context.rotate(angleInRadians);
      context.fillStyle = "red";
      context.fillRect(100,100,50,50);

   }
Simple rotation transformation

Figure 2-12. Simple rotation transformation

We get a result this time, but it will probably differ from what you expect. The red box is rotated, but it looks like the canvas was rotated with it. The entire canvas did not rotate, only the portion drawn after the context.rotate() function was called. So, why did our square both rotate and move off to the left of the screen? The origin of the rotation was set at the “nontranslated” 0,0 position, resulting in the square rotating from the top left of the entire canvas.

Example 2-8 offers a slightly different scenario: draw a black box first, then set the rotation transform, and finally draw the red box again. See the results in Figure 2-13.

Example 2-8. Rotation and the Canvas state

function drawScreen() {

      //draw black square
      context.fillStyle = "black";
      context.fillRect(20,20,25,25);

      //now draw a red square
      context.setTransform(1,0,0,1,0,0);
      var angleInRadians = 45 * Math.PI / 180;
      context.rotate(angleInRadians);
      context.fillStyle = "red";
      context.fillRect(100,100,50,50);

   }
Rotation and the Canvas state

Figure 2-13. Rotation and the Canvas state

The small black square was unaffected by the rotation, so you can see that only the shapes drawn after the context.rotate() function was called were affected.

Again, the red box was moved far off to the left. To reiterate, this occurred because the canvas did not know what origin to use for the rotation. In the absence of an actual translated origin, the 0,0 position setting is applied, resulting in the context.rotate() function rotating “around” the 0,0 point, which brings us to our next lesson.

Lesson 2: We must “translate” the point of origin to the center of our shape to rotate it around its own center

Let’s change Example 2-8 to rotate the red square 45 degrees while keeping it in its current location.

First, we take the numbers we applied to the fillRect() function call to create a few variables to hold the red square’s attributes. This is not necessary, but it will make the code much easier to read and change later:

var x = 100;
var y = 100;
var width = 50;
var height = 50;

Next, using the context.translate() function call, we must change the origin of the canvas to be the center of the red square we want to rotate and draw. This function moves the origin of the canvas to the accepted x and y locations. The center of our red square will now be the desired top-left corner x location for our object (100), plus half the width of our object. Using the variables we created to hold attributes of the red square, this would look like:

x+0.5*width

Next, we must find the y location for the origin translation. This time, we use the y value of the top-left corner of our shape and the height of the shape:

y+.05*height

The translate() function call looks like this:

context.translate(x+.05*width, y+.05*height)

Now that we have translated the canvas to the correct point, we can do our rotation. The code has not changed:

context.rotate(angleInRadians);

Finally, we need to draw our shape. We cannot simply reuse the same values from Example 2-8 because the canvas origin point has moved to the center of the location where we want to draw our object. You can now consider 125,125 as the starting point for all draw operations. We get 125 for x by taking the upper-left corner of the square (100) and adding half its width (25). We do the same for the y origin position. The translate() method call accomplishes this.

We will need to draw the object starting with the correct upper-left coordinates for x and y. We do this by subtracting half the width of our object from the origin x, and half the height of our object from the origin y:

context.fillRect(-0.5*width,-0.5*height, width, height);

Why do we do this? Figure 2-14 illustrates the situation.

Consider that we want to draw our square starting at the top-left corner. If our origin point is at 125,125, the top left is actually 100,100. However, we have translated our origin so the canvas now considers 125,125 to be 0,0. To start our box drawing at the nontranslated canvas, we have to start at –25,–25 on the “translated” canvas.

This forces us to draw our box as though the origin is at 0,0, not 125,125. Therefore, when we do the actual drawing of the box, we must use these coordinates, as shown in Figure 2-15.

The newly translated point

Figure 2-14. The newly translated point

Drawing with a translated point

Figure 2-15. Drawing with a translated point

In summary, we needed to change the point of origin to the center of our square so it would rotate around that point. But when we draw the square, we need our code to act as though the (125,125) point is actually (0,0). If we had not translated the origin, we could have used the (125,125) point as the center of our square (as in Figure 2-14). Example 2-9 demonstrates how this works, creating the result shown in Figure 2-16.

Example 2-9. Rotation around the center point

function drawScreen() {

      //draw black square
      context.fillStyle = "black";
      context.fillRect(20,20 ,25,25);

      //now draw a red square
      context.setTransform(1,0,0,1,0,0);
      var angleInRadians = 45 * Math.PI / 180;
      var x = 100;
      var y = 100;
      var width = 50;
      var height = 50;
      context.translate(x+.5*width, y+.5*height);
      context.rotate(angleInRadians);
      context.fillStyle = "red";
      context.fillRect(-.5*width,-.5*height , width, height);

   }
Rotation around the center point

Figure 2-16. Rotation around the center point

Let’s look at one final rotation example. Example 2-10 takes Example 2-9 and simply adds four separate 40×40 squares to the canvas, rotating each one slightly. The result is shown in Figure 2-17.

Example 2-10. Multiple rotated squares

function drawScreen() {

      //now draw a red square
      context.setTransform(1,0,0,1,0,0);
      var angleInRadians = 45 * Math.PI / 180;
      var x = 50;
      var y = 100;
      var width = 40;
      var height = 40;
      context.translate(x+.5*width, y+.5*height);
      context.rotate(angleInRadians);
      context.fillStyle = "red";
      context.fillRect(-.5*width,-.5*height , width, height);

      context.setTransform(1,0,0,1,0,0);
      var angleInRadians = 75 * Math.PI / 180;
      var x = 100;
      var y = 100;
      var width = 40;
      var height = 40;
      context.translate(x+.5*width, y+.5*height);
      context.rotate(angleInRadians);
      context.fillStyle = "red";
      context.fillRect(-.5*width,-.5*height , width, height);

      context.setTransform(1,0,0,1,0,0);
      var angleInRadians = 90 * Math.PI / 180;
      var x = 150;
      var y = 100;
      var width = 40;
      var height = 40;
      context.translate(x+.5*width, y+.5*height);
      context.rotate(angleInRadians);
      context.fillStyle = "red";
      context.fillRect(-.5*width,-.5*height , width, height);
      context.setTransform(1,0,0,1,0,0);
      var angleInRadians = 120 * Math.PI / 180;
      var x = 200;
      var y = 100;
      var width = 40;
      var height = 40;
      context.translate(x+.5*width, y+.5*height);
      context.rotate(angleInRadians);
      context.fillStyle = "red";
      context.fillRect(-.5*width,-.5*height , width, height);

}
Multiple rotated squares

Figure 2-17. Multiple rotated squares

Next, we will examine scale transformations.

Scale Transformations

The context.scale() function takes in two parameters: the first is the scale attribute for the x-axis, and the second is the scale attribute for the y-axis. The value 1 is the normal scale for an object. Therefore, if we want to double an object’s size, we can set both values to 2. Using the code below in drawScreen() produces the red square shown in Figure 2-18:

context.setTransform(1,0,0,1,0,0);
context.scale(2,2);
context.fillStyle = "red";
context.fillRect(100,100 ,50,50);
A simple scaled square

Figure 2-18. A simple scaled square

If you test this code, you will find that scale works in a similar manner as rotation. We did not translate the origin of the scale point to double the size of the square; rather, we used the top-left corner of the canvas as the origin point. The result is that the red square appears to move farther down and to the left. What we would like is for the red square to remain in place and to scale from its center. We do this by translating to the center of the square before we scale, and by drawing the square around this center point (just as we did in Example 2-9). Example 2-11 produces the result shown in Figure 2-19.

Example 2-11. Scale from the center point

function drawScreen() {

      //now draw a red square
      context.setTransform(1,0,0,1,0,0);
      var x = 100;
      var y = 100;
      var width = 50;
      var height = 50;
      context.translate(x+.5*width, y+.5*height);
      context.scale(2,2);
      context.fillStyle = "red";
      context.fillRect(-.5*width,-.5*height , width, height);

   }
Scale from the center point

Figure 2-19. Scale from the center point

Combining Scale and Rotation Transformations

If we want to both scale and rotate an object, Canvas transformations can easily be combined to achieve the desired results (as shown in Figure 2-20). Let’s look in Example 2-12 at how we might combine them by using scale(2,2) and rotate(angleInRadians) from our previous examples.

Example 2-12. Scale and rotation combined

function drawScreen() {
      context.setTransform(1,0,0,1,0,0);
      var angleInRadians = 45 * Math.PI / 180;
      var x = 100;
      var y = 100;
      var width = 50;
      var height = 50;
      context.translate(x+.5*width, y+.5*height);
      context.scale(2,2);
      context.rotate(angleInRadians);
      context.fillStyle = "red";
      context.fillRect(-.5*width,-.5*height , width, height);

   }
Scale and rotation combined

Figure 2-20. Scale and rotation combined

Example 2-13 also combines rotation and scale, this time using a rectangle. Figure 2-21 reveals what it creates.

Example 2-13. Scale and rotate a nonsquare object

function drawScreen() {

      //now draw a red rectangle
      context.setTransform(1,0,0,1,0,0);
      var angleInRadians = 90 * Math.PI / 180;
      var x = 100;
      var y = 100;
      var width = 100;
      var height = 50;
      context.translate(x+.5*width, y+.5*height);
      context.rotate(angleInRadians);
      context.scale(2,2);

      context.fillStyle = "red";
      context.fillRect(-.5*width,-.5*height , width, height);

   }
Scale and rotate a nonsquare object

Figure 2-21. Scale and rotate a nonsquare object

The bounding box of a complex shape

Figure 2-22. The bounding box of a complex shape

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