Free Trial

Safari Books Online is a digital library providing on-demand subscription access to thousands of learning resources.

  • Create BookmarkCreate Bookmark
  • Create Note or TagCreate Note or Tag
  • PrintPrint
Share this Page URL
Help

4. Hacking Your Graphics with Canvas and... > Hack 37. Draw Shapes on Your HTML5 <...

Hack 37. Draw Shapes on Your HTML5 <canvas> Tag

Flash became insanely popular because of the flexibility it brought to the browser. With Flash the Web was free from decorating DOM elements and became a platform for real drawing and animation. HTML5 brings this same type of flexibility and power directly to the DOM with the HTML5 <canvas> tag. This hack starts us off slow by walking through the creation of basic shapes on a canvas.

The <canvas> tag provides you with a blank slate to create your imagery. In order to do this you first need to create a <canvas> tag in the DOM, and then identify the context. The <canvas> tag is created as a DOM element:

    <canvas id="myCanvas" width="200" height="200"></canvas>

This basic <canvas> tag will be presented as a 200 × 200-px empty block on the page. To add to it, we need to identify the context:

    var myCanvas = document.getElementById('myCanvas')
    var myCtx = myCanvas.getContext('2d');

Notice that we identify the '2d' context which may seem to imply that there would also be a '3d' context, but don’t be fooled: “3d” isn’t really addressed by the <canvas> tag; it has only an x- and y-axis. Now that we have the context identified, we have a host of APIs at our fingertips.

Drawing to a <canvas> tag is all about the '2d' context and finding the appropriate coordinates on the grid. Generally, one pixel on the screen correlates to one point in the canvas (this value can vary when you zoom in or out on a small screen such as on a mobile browser, or when your element is resized with CSS). The key point on our grid is (0,0) or the origin, which is the top-lefthand corner of our canvas. Our canvas is 200 × 200, which means it contains 200 points on the x-axis and 200 points on the y-axis. Figure 4-1 shows how our canvas would appear with grid lines on the x- and y-axes over 10 points.

The 200 × 200 <canvas> tag with grid markers every tenth point on both the x- and y-axes
Figure 4-1. The 200 × 200 <canvas> tag with grid markers every tenth point on both the x- and y-axes

Drawing Rectangles

We’ll start with one of the simplest shapes: the rectangle. These are easy to draw into the context of our <canvas> tag. The '2d' context gives us access to the API to draw three basic types of rectangles:

fillRect

Draws a rectangle with a solid color fill

strokeRect

Draws a rectangle that has a border but no fill

clearRect

Clears a rectangle-shaped transparency that removes any imagery or fills in the defined area

Taking our sample canvas from before, let’s combine these three shapes onto our <canvas> tag:

    var myCanvas = document.getElementById('myCanvas')
    var myCtx = myCanvas.getContext('2d');
    myCtx.strokeRect(10,10, 180, 180);
    myCtx.clearRect(50,50, 100, 100);

The preceding code laid on top of our <canvas> tag looks like Figure 4-2.

The 200 × 200 canvas demonstrating the different rectangle APIs in the <canvas> tag
Figure 4-2. The 200 × 200 canvas demonstrating the different rectangle APIs in the <canvas> tag

Each of the three APIs follows the same pattern. They are passed four parameters: the x and y coordinates, along with the width and height of the rectangle.

Drawing Paths

Rectangles are just the tip of the iceberg when it comes to drawing on a canvas. Most imagery is produced by combining a series of lines. Like all methods in the <canvas> tag, these drawing APIs are available on the '2d' context. Paths require a few steps to start and complete a drawing. To start a drawing (a single path or series of paths), we use this method:

    myContext.beginPath();

This method takes no arguments; it simply initiates a drawing. Once the path has begun, we need to determine where we are going to start and end the path. To start the path, we will use the moveTo method. This is similar to determining where you would move your pencil on a piece of drawing paper. Think of it as picking up a pencil and putting it down directly on your starting point. From there, we will use the lineTo method to determine where our line will end. Here is the first line of our grid:

myContext.beginPath();
myContext.moveTo(0,0);
myContext.lineTo(200,0);

At this point our canvas will still be blank, as we have not yet closed our path. To close the path we use the following method:

    myContext.closePath();

Now we have one line on our canvas. To create our grid, we want to draw multiple lines within our path. To accomplish this, we will begin the path, and then create a series of moveTo and lineTo methods. Once we have all our grid lines, we will write them to the canvas with our stroke method. Our code will look something like this:

   var myCanvas = document.getElementById('myCanvas')
    var myContext = myCanvas.getContext('2d');
    var ctx = myContext;

    myContext.beginPath();
    for(i=0; i<201; i++){

    myContext.moveTo(0,i);
    myContext.lineTo(200,i);
    i+=10;
    }

    for(i=0; i<201; i++){

    myContext.moveTo(i,0);
    myContext.lineTo(i, 200);
    i+=10;
    }
    myContext.stroke();

Paths have a number of different JavaScript APIs that create different line effects. In many cases we may have a few lines that we want to connect and consequently fill the area. To accomplish this we can simply call the following method:

myContext.fill();

Smile, the Canvas Loves You!

We can get pretty far with straight lines in our drawings, but we can use the canvas to draw arcs as well. Remember, the <canvas> tag will always be a square, but we can draw any shape inside the square. To draw an arc on the canvas, call the following method off the canvas context:

arc(x, y, radius, startAngle, endAngle, anticlockwise);

As illustrated in the preceding code, a number of arguments are passed into the arc method. The first two are the coordinates for the arc’s center, followed by the arc radius. The startAngle and endAngle parameters declare the start and end points of the arc in radians, which are measured from the x-axis. The final optional anticlockwise parameter, when set to true, draws the arc in a counterclockwise direction. The default is false, which would draw the arc in a clockwise direction.

Looking back at the radius argument, we want to make a special note. In CSS, we are comfortable with declaring values in degrees, but in this case the arc radius is measured in radians. It’s quite common to see an inline conversion from radians to degrees using the JavaScript math equation for pi:

myRadians = (Math.PI/180)*degrees

Let’s put this to good use by creating something recognizable on the <canvas> tag. When I think of circles I think of two things: smiley faces and bombs. To keep the violence level down, we’ll work on the smiley face in this chapter. Using a similar 200 × 200 <canvas> tag let’s center our outer circle directly in the middle of our tag, and then draw our head:

    smileCtx.beginPath();
    smileCtx.arc(100,100,99,0,Math.PI*2);

We now have a canvas with a circle on it, as shown in Figure 4-3.

The <canvas> tag with a circle centered on the element
Figure 4-3. The <canvas> tag with a circle centered on the element

This isn’t very exciting. So next we will add the mouth. For this we will use the moveTo method, and then draw a half circle (notice that the radius will be PI instead of PI*2 as it was for the full circle):

    smileCtx.moveTo(170,100);
    smileCtx.arc(100,100,70,0,Math.PI);   // Mouth

The last two components are the eyes. Since we want our eyes to be solid fills, we need to make separate strokes for each of them so that we can apply the fill. The first step to accomplish this is to close the current stroke. We will then start a new stroke, move to a new start point, draw a new circle, and call our fill parameter for each eye:

    smileCtx.stroke();
    smileCtx.beginPath();
    smileCtx.moveTo(60, 65);
    smileCtx.arc(60,65,12,0,Math.PI*2);  // Left eye
    smileCtx.fill();

Let’s put all this code together, and see our masterpiece:

    var mySmile = document.getElementById('mySmile')
    var smileCtx = mySmile.getContext('2d');

    smileCtx.beginPath();
    smileCtx.arc(100,100,99,0,Math.PI*2); // head
    smileCtx.moveTo(170,100);
    smileCtx.arc(100,100,70,0,Math.PI);   // Mouth
    smileCtx.stroke();

    smileCtx.beginPath();
    smileCtx.moveTo(60, 65);
    smileCtx.arc(60,65,12,0,Math.PI*2);  // Left eye
    smileCtx.fill();

    smileCtx.beginPath();
    smileCtx.moveTo(140,65);
    smileCtx.arc(140,65,12,0,Math.PI*2);  // Right eye
    smileCtx.fill();

Our canvas now holds all three strokes to form the face, as shown in Figure 4-4.

The 200 × 200 <canvas> tag with the smiley face
Figure 4-4. The 200 × 200 <canvas> tag with the smiley face

Advanced Drawing

We’ve plowed right through lines and arcs, but many illustrations call for lines that can’t be accomplished by either of these shapes. The Canvas specification includes two additional tools for creating custom shapes:

quadraticCurveTo(cp1x, cp1y, x, y);
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);

Each of these methods has control points and an ending x,y point. The control points determine the curvature of the path. The bezierCurveTo method has a second control point for an uneven curvature. Additional information about the implementation of each method is available in the W3C spec.

  • Safari Books Online
  • Create BookmarkCreate Bookmark
  • Create Note or TagCreate Note or Tag
  • PrintPrint