O'Reilly logo

HTML5 Hacks by Jeff Burtoft, Jesse Cravens

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Chapter 4. Hacking Your Graphics with Canvas and SVG

Over the past decade Adobe Flash became very popular because it allowed us to create and manipulate imagery directly within our web pages. This demand resulted in development of the Canvas specification.

The <canvas> tag is one of the most flexible of the new HTML5 tags. This has made the <canvas> tag the new foundation for web-based gaming and other interactive components. The <canvas> tag itself, much like the name, is a blank slate. It’s a “drawing” surface that gives developers the freedom and flexibility to create imagery that can blend with and complement the rest of the DOM (Document Object Model).

The “underdog” HTML5 illustration tool presented in this chapter is Scalable Vector Graphics (SVG). SVG is probably the oldest technology discussed in this book—it has been supported in some browsers for the past 12 years as it was first supported by Microsoft’s Internet Explorer version 6. Rarely does anyone have anything nice to say about IE 6, but in terms of its support of SVG it was ahead of its time.

SVG is an XML markup language that is very similar to HTML, and will probably be pretty easy for those of you who are experienced with HTML. SVG basically does for graphics what HTML did for text. SVG is lightweight and flexible, and can scale to any size with the same lightweight file.

Although there may seem to be a lot of overlap between these two languages, you will quickly see where each technology shines. It’s clear why the HTML5 family encompasses both of these powerhouse visual tools.

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.

Hack 38. Apply Styles to Your Canvas Elements

We don’t live in a black-and-white Web, which makes it essential to be able to apply colors and styles to your <canvas> tag elements. Style your canvas elements with this familiar CSS syntax.

If you need to catch up on how to create shapes, strokes, or fills on your <canvas> tag, read [Hack #37].

Shapes and strokes have little effect on our applications if we can’t apply color and styles to them. The specification for canvas styles borrows heavily from CSS, so a lot of the syntax should be familiar to you.

Color

Canvas elements can be colored with any CSS color value style, and they even support transparency with RGBA and HSPA colors. Also, canvas strokes and shapes have a default color value of black.

Let’s look at a code example for drawing a grid of lines across a 200 × 200 <canvas> tag:

    var myCanvas = document.getElementById('myCanvas')
    var myContext = myCanvas.getContext('2d');
    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();

This example draws vertical lines every 10 points, and then loops around again to draw horizontal lines every 10 points. As stated previously, the default color for each line is black. To give the look of graph paper we want to make the lines a light blue color. We can accomplish this by adding a single line of code:

    myContext.strokeStyle = '#99C4E5';

Since the whole grid is accomplished through one stroke, we only need to declare the style once. To add a bit of depth to our grid we will make the horizontal lines slightly darker than the vertical lines. Since we are going to style the lines in two different ways, we need to add a few lines of JavaScript to our code to separate our illustration into two different strokes. To accomplish this, we will end the stroke after the first for loop and then start a new stroke for the second for loop:

    var myCanvas = document.getElementById('myCanvas2')
    var myContext = myCanvas.getContext('2d');
    myContext.strokeStyle = '#1487E0';

    myContext.beginPath();
    for(i=0; i<201; i++){
        myContext.moveTo(0,i);
        myContext.lineTo(200,i);
        i+=10;
    }
    myContext.stroke();
    myContext.beginPath();
    myContext.strokeStyle = '#B1CADD';

    for(i=0; i<201; i++){
        myContext.moveTo(i,0);
        myContext.lineTo(i, 200);
        i+=10;
    }

As soon as we started the second stroke with the beginPath method, we set a new, darker stroke style for the horizontal lines.

There are two different methods for adding color to your shapes. strokeStyle applies to lines and the outline of shapes, and fillStyle applies to shapes or strokes that have a fill applied to them. It’s important to note that once you set a stroke or fill style, the setting will persist in the context until it is changed back to the original value, or until it is set to a new value.

Gradients

Just as with other HTML5 elements, adding gradients can provide for deep visual depth, and can be quite useful. Let’s take a look at our example of a simple black-and-white smiley face, before applying a few gradients to spice it up:

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

The preceding code provides us with the basic smiley face shown in Figure 4-5.

The <canvas> tag with the smiley face illustration
Figure 4-5. The <canvas> tag with the smiley face illustration

As every child who wasn’t raised by wolves knows, smiley faces are supposed to be yellow. Let’s redraw our smiley face with a yellow background:

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

    smileCtx.beginPath();

    smileCtx.fillStyle = '#F1F42E';
    smileCtx.arc(100,100,99,0,Math.PI*2); // head

    smileCtx.stroke();
    smileCtx.fill();

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

    smileCtx.beginPath();
    smileCtx.fillStyle = 'black';
    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();

This gives us a more iconic version of our smiley face, as shown in Figure 4-6.

The same canvas smiley face with a yellow fill on the head circle
Figure 4-6. The same canvas smiley face with a yellow fill on the head circle

In order to accommodate the introduction of color, we had to make a few changes to our code. First, we extracted the mouth from the same stroke that made the head so that the fill would not overwrite the line used for the mouth. Then we added a fill method to the end of the head circle to color it yellow. The last change we made was to reset the fill color back to black for the eyes. Again, once we set the style, we needed to reset it to black to return to the default value.

Now, to prove that we have some artistic talent, we will change our yellow color to a yellow gradient. We can apply two types of gradients:

createLinearGradient(x1,y1,x2,y2)
createRadialGradient(x1,y1,r1,x2,y2,r2)

The createLinearGradient method is passed four different arguments: the start point (x1,y1) and the end point (x2,y2) of the gradient.

The createRadialGradient method is passed six arguments. The first three define an inner circle with coordinates (x1,y1) and one radius (r1) and an outer circle with coordinates and a second radius.

Our example will use a radial gradient to give our smiley face three-dimensional depth. First we will set our gradient to a variable, and then we will add a series of color stops to the gradient. In our code example, we’ll replace the fillStyle with our gradient:

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

    var radgrad = smileCtx.createRadialGradient(100,100,10,100,100,100);
    radgrad.addColorStop(.5, 'rgba(247,241,192,1)');
    radgrad.addColorStop(1, 'rgba(244,225,56,1)');
    smileCtx.beginPath();

    smileCtx.fillStyle = radgrad;
    smileCtx.arc(100,100,99,0,Math.PI*2); // head

    smileCtx.stroke();
    smileCtx.fill();

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

    smileCtx.beginPath();
    smileCtx.fillStyle = 'black';
    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();

We have simply replaced our fill color with the gradient as a fill. This gives us the added depth we want to make our smiley face stick out from the crowd (see Figure 4-7).

The smiley face canvas drawing with a gradient in place of a solid fill color
Figure 4-7. The smiley face canvas drawing with a gradient in place of a solid fill color

Additional Styles

You can accomplish a transparency effect with a color of your choosing, or you can apply the transparency globally to a stroke. To adjust the transparency level, use the globalAlpha method:

globalAlpha = .2;

Unlike the color styles, the globalAlpha method only applies to the current stroke. Once a new stroke is started, the globalAlpha method resets to 1.

Because they play such a large role in illustrations, lines are given additional control values in your <canvas> tag. You can set the following values for a stroke on your <canvas> tag:

lineWidth

A numerical value that represents the width in points

lineCap

The shape of the end of a line, which can be declared as butt, round, or square

lineJoin

The shape of a line joint, which can be declared as round, bevel, or miter

miterLimit

Determines how far the outside connection point can be placed from the inside connection point, when the lineJoin type of miter is selected

Patterns and shadows can also be applied to canvas elements, and they follow similar syntax to CSS implementations. For details on these features and more, see the W3C specification on the <canvas> tag.

Hack 39. Style Canvas Elements with Image Files

Shapes in a <canvas> tag have some of the same controls as other page elements. In this hack, you’ll learn how to take your canvas illustrations one step further by utilizing images as fills.

The Canvas specification gives you a lot of flexibility to create your HTML5 illustrations. Other hacks have covered basic shapes, colors, gradients, and other styles, so this hack will focus on importing another object for use on your canvas element.

The Basic Fill

For details on fills and other styles, see [Hack #38].

To illustrate the use of an image as a fill, we’ll start by looking at a smiley face example with a basic yellow color fill for the head (see Figure 4-8).

The 200 × 200 <canvas> tag with a drawing of a smiley face
Figure 4-8. The 200 × 200 <canvas> tag with a drawing of a smiley face

We set the background color by adding a color fill to the circle that makes up the head. Once the stroke is started, it’s a simple line of code:

 smileCtx.fillStyle = '#F1F42E';

Our end result will have a simple image used as a repeating background (see Figure 4-9).

The 200 × 200 <canvas> tag with a drawing of a smiley face and a repeating heart background image
Figure 4-9. The 200 × 200 <canvas> tag with a drawing of a smiley face and a repeating heart background image

To change that solid color to an image, we will use a very similar API:

smileCtx.fillStyle = myPattern;

You can see in the preceding code that we are using the same API for an image background as we are for a fill color (similar to the background attribute in a CSS declaration). However, a bit of additional overhead is required when using an image.

Using an Image as a Fill

In JavaScript, to use an image you first must have a reference to it. In our case, we will start by creating the image dynamically, and then setting its src attribute:

var img = new Image();
img.src = '/assets/img/heart.png';

The image we are using is the small icon-size image shown in Figure 4-10.

The small image used as a repeating background
Figure 4-10. The small image used as a repeating background

That was easy enough; we now have a variable called img that references our image file. The second step is to set that image as a pattern to be utilized by the <canvas> tag:

    var myPattern = smileCtx.createPattern(img,'repeat');
    smileCtx.fillStyle = myPattern;

To accomplish this, we used a canvas method called createPattern. This requires two parameters: the first is the reference to the image file, and the second is our DOMstring repetition. Similar to a CSS implementation, we can set the DOMstring repetition to repeat, repeat-x, repeat-y, or no-repeat. If no value is specified, it defaults to repeat.

Now let’s put all of this together and see what it looks like. Here is a view of the code used to generate our smiley face with the image as a background:

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

    // create new image object to use as pattern
    var img = new Image();
    img.src = '/assets/img/heart.png';
    // create pattern
    var myPattern = smileCtx.createPattern(img,'repeat');
    smileCtx.fillStyle = myPattern;
    smileCtx.arc(100,100,99,0,Math.PI*2); // head
    smileCtx.stroke();
    smileCtx.fill();

    smileCtx.beginPath();
    smileCtx.moveTo(170,100);
    smileCtx.arc(100,100,70,0,Math.PI);   // Mouth
    smileCtx.stroke();
    smileCtx.beginPath();
    smileCtx.fillStyle = 'black';
    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();

If we were to run this code, we would probably be disappointed with the results. In most cases, our smiley face would look like Figure 4-11.

The smiley face canvas rendering with the background image set as in the previous code sample
Figure 4-11. The smiley face canvas rendering with the background image set as in the previous code sample

Can you identify the problem? Think about the load time. The canvas is taking advantage of real-time data. In the preceding sample, we created the image and then set it as a background immediately. Since the pattern failed, the canvas fill reverted back to its default state of black for the fill color. The problem has to do with the availability of the image data, which in our case hasn’t been loaded yet.

To solve this problem we will add a few lines of JavaScript that wait for the image to load before we execute the necessary canvas code. Browsers have supported the image onload event for years. In this example we’ll use the image onload event to know when we have the necessary data loaded:

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

    // create new image object to use as pattern
    var img = new Image();
    img.src = '/assets/img/heart.png';
    img.onload = function(){

    // create pattern
    var myPattern = smileCtx.createPattern(img,'repeat');
    smileCtx.fillStyle = myPattern;
    smileCtx.arc(100,100,99,0,Math.PI*2); // head
    smileCtx.stroke();
    smileCtx.fill();

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

    smileCtx.beginPath();
    smileCtx.fillStyle = 'black';
    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();
    }

Now we’re sure that our image data has loaded, and the <canvas> tag can take full advantage of the image for use in its pattern background.

Easy Image Data

Adding image onloads around whole segments of code can sometimes be cumbersome. A nice shortcut available in HTML5 browsers is the use of inline image data. We can easily remove the onload event from the preceding example and simply reference the image data. Since the image data was loaded when the page was loaded, there is no need to wait for the onload event to fire before we attempt to use the image. Our new code would look like this:

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

    // create new image object to use as pattern
    var img2 = new Image();
    img2.src = 'data:image/png;base64,iVBORw0K... image data here
...f5v038BfQ3g/3mcvqgAAAAASUVORK5CYII=';

    // create pattern
    var myPattern = smileCtx.createPattern(img2,'repeat');
    smileCtx.fillStyle = myPattern;
    smileCtx.arc(100,100,99,0,Math.PI*2); // head
    smileCtx.stroke();
    smileCtx.fill();

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

    smileCtx.beginPath();
    smileCtx.fillStyle = 'black';
    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();

It may not make sense to utilize the Base64 version of your image in all cases, since it results in added weight in the initial page load, but sometimes it may be appropriate in order to utilize and simplify your code. It’s a good practice to have multiple implementation methods to choose from.

Hack 40. Use the HTML5 <canvas> Tag to Create High-Res, Retina-Display-Ready Media

When Apple first introduced the Retina display on the iPhone 4, parts of the Web started to look pretty shabby. The display’s higher resolution made your quick-loading “web-ready” images look pixelated. In general, higher-resolution images mean longer load times. This hack uses the HTML5 <canvas> tag to provide Retina-ready imagery without the added weight.

There is a problem with our Retina screens. They look great (few people will debate that), but the way in which they accomplish this has caused a lot of problems for web developers. Apple first introduced the Retina display with the iPhone 4, in an attempt to solve two problems: create a display in which the pixels were indistinguishable to the naked eye, and not make iOS and Apple apps look like crap. To do this, Apple marked the pixel density much higher than was necessary, and in fact gave the display a density that was evenly divisible by the previous iPhone screen density. This enabled Apple to update all the visual assets of the iOS SDK and the iOS operating system to a higher resolution, and simply downsize it for older, less dense screens. For all the other assets in the Apple apps, the company used a method called pixel doubling to help the assets remain at the proper size.

Assets such as images and media on the Web fall prey to pixel doubling. This makes our web pages look pixelated and jagged. The common solution to this problem is to utilize images with twice the pixel resolution, which leaves us with images that are larger and web pages that take significantly longer to load.

In Comes the <canvas> Tag

The <canvas> tag is a drawing space for vector illustrations. Since the <canvas> tag is created using a set of definitions, the size of the illustration is inconsequential to the amount of data that is necessary to create it (unlike images that required the transfer of additional data to accommodate more pixels). This being the case, we can make our <canvas> tag Retina-ready without any additional page weight.

Let’s start by loading a simple example of a smiley face drawn out on a 200 × 200-point <canvas> tag. Here is the code for creating our example:

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

    smileCtx.beginPath();

    smileCtx.fillStyle = '#F1F42E';
    smileCtx.arc(100,100,99,0,Math.PI*2); // head

    smileCtx.stroke();
    smileCtx.fill();

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

    smileCtx.beginPath();
    smileCtx.fillStyle = 'black';
    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 smiley face looks great on a standard display, but it’s pretty jagged on the Retina display. Figure 4-12 shows how our canvas image looks on the iPhone 3Gs and the iPhone 4.

The canvas image displayed on the iPhone 3GS (left), and on the iPhone 4 with a Retina display (right)
Figure 4-12. The canvas image displayed on the iPhone 3GS (left), and on the iPhone 4 with a Retina display (right)

In order to have the illustration be smooth for the Retina display, we need to counteract the pixel doubling that is taking place. To accomplish this, we will add a few simple lines of code to our JavaScript:

    if(window.devicePixelRatio == 2) {
        mySmile.setAttribute('width', 400);
        mySmile.setAttribute('height', 400);
        smileCtx6.scale(2, 2);
        }

We will insert this into our code right after we declare our context, but before we start to apply our elements to the <canvas> tag. In essence, we have detected when pixel doubling is being applied (by checking the device pixel ratio) and then doubled the size of our <canvas> tag. These lines of code will result in the big, fat smiley face shown in Figure 4-13.

The smiley face twice the size it was before, thanks to the new JavaScript code
Figure 4-13. The smiley face twice the size it was before, thanks to the new JavaScript code

Now we need to rescale our <canvas> tag to fit our original page space. In order to have the page render all the pixels in half the size, we will set our canvas to 400 and then use CSS to shrink it back down to 200 px. Let’s add this CSS to the top of our page:

#mySmile{
    height: 200px;
    width: 200px;
}

With just a few lines of code we have essentially Retina-enabled our <canvas> tag without having to increase the page weight significantly. Let’s go back to our iPhone 3GS–iPhone 4 comparison to see our results (see Figure 4-14).

The canvas image displayed on the iPhone 3GS (left), and on the iPhone 4 with a Retina display after the addition of the new JavaScript code
Figure 4-14. The canvas image displayed on the iPhone 3GS (left), and on the iPhone 4 with a Retina display after the addition of the new JavaScript code

We’ve improved the experience for our Retina-display users without affecting the rest of our user base. You can apply this technique to any <canvas> tag, whether it is a page illustration or a canvas element used as a CSS background image. The only time you will not benefit from using this technique is when you import an image into your <canvas> tag that doesn’t support the Retina display’s higher resolution.

Hack 41. Accelerate Animation with Canvas Drawings

Use of the <canvas> tag is often one of the most efficient ways to create animations in your web applications. This hack digs into the nitty-gritty of creating animations while using the <canvas> tag.

Clean animation can make or break your web applications. Native applications on desktop and mobile devices have raised users’ expectations: if your web application fails to include clean, concise animations, users will often write it off as being a poorly performing app.

Canvas animation can be a powerful tool for web animations. As more and more browser makers enable the GPU for canvas animations, it becomes even more beneficial to perform your animations with a canvas element.

Write and Clean

Animation on a <canvas> tag is reminiscent of early cartoon animations where each frame is drawn out and then displayed in the correct order and at the determined frame rate. Canvas animation basically consists of these three steps:

  1. Draw on the canvas.

  2. Erase what you just drew.

  3. Repeat steps 1 and 2 until the animation is complete.

In JavaScript, when things need to be called over and over again we often use methods such as setTimeout and setInterval to call our drawing methods. The problem with each of these methods is they need to be set to a specific amount of time. If we set that time to, say, 100 milliseconds, we would never be able to achieve a frame rate higher than 10 frames per second.

A powerful new standard has been introduced to address this issue with the <canvas> tag: the requestAnimationFrame method. With this method, you are asking the browser to render the next frame as soon as it is available for rendering, as opposed to attempting to render at a fixed interval. The goal of requestAnimationFrame is 60 frames per second, but it doesn’t fail if it can’t render that quickly; it simply renders as soon as it can. Note that this method isn’t limited to use in canvas animations; it’s available for any web drawing technology, including WebGL.

Smile, You’re Being Animated!

Let’s take a good look at an example of a canvas animation. If you’ve worked your way through the previous hacks in this chapter you have seen the smiley face examples. Each example drew the smiley face on a 200 × 200 canvas element. For this illustration we will draw it on a much larger canvas to give us room to move. Let’s start by dropping our <canvas> tag onto the page:

<canvas id="moveSmile" width="800" height="200"></canvas>

Now that we have a big, fat, blank canvas, we will draw the smiley face on top of it. To do this, we’ll pull in a few lines of JavaScript to build our page elements:

       var canvas  = document.getElementById("moveSmile");
       var smileCtx = canvas.getContext("2d");

        smileCtx.beginPath();

        smileCtx.fillStyle = '#F1F42E';
        smileCtx.arc(100,100,99,0,Math.PI*2); // head

        smileCtx.stroke();
        smileCtx.fill();

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

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

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

Our code simply draws out this smiley face on the lefthand side of the <canvas> tag. For illustration purposes, a 1 px border has been added to the <canvas> tag so that we can see the boundaries (see Figure 4-15).

The smiley face illustration on the lefthand side of an 800-point canvas
Figure 4-15. The smiley face illustration on the lefthand side of an 800-point canvas

Going back to our three-step process, once we draw our illustration we need to erase what we’ve drawn:

smileCtx.clearRect(0, 0, 800, 200); //smileCtx is the 2d context

For simplicity I’m erasing the whole canvas, but to optimize performance you should focus on erasing what is changing for the next frame. In the preceding method I am clearing the whole canvas by setting the clearRect coordinates from the top-lefthand corner of the canvas to the bottom-righthand corner. This erases a rectangular shape the size of the canvas.

Our canvas should now be void of illustration, as shown in Figure 4-16.

The 800 × 200 <canvas> tag after the clearRect method has cleared the entire canvas context
Figure 4-16. The 800 × 200 <canvas> tag after the clearRect method has cleared the entire canvas context

Now, for step 3 we will redraw our smiley face, but we will move it slightly to the right. In order to do this, we will move the x position of both our moveTo methods and our element start position (arc in this case).

To accomplish this, we will replace each number with a simple equation to generate the proper x coordinate each time the element is drawing:

    x+startingposition

Our code will now look like this:

        var x = 0;
        smileCtx6.beginPath();

        smileCtx6.fillStyle = '#F1F42E';
        smileCtx6.arc(x+100,100,99,0,Math.PI*2); // head

        smileCtx6.stroke();
        smileCtx6.fill();

        smileCtx6.beginPath();
        smileCtx6.moveTo(x+170,100);
        smileCtx6.arc(x+100,100,70,0,Math.PI);   // Mouth
        smileCtx6.stroke();

        smileCtx6.beginPath();
        smileCtx6.fillStyle = 'black';
        smileCtx6.moveTo(x+60, 65);
        smileCtx6.arc(x+60,65,12,0,Math.PI*2);  // Left eye
        smileCtx6.fill();

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

For the preceding code x is set to 0, but in order to move the smiley face across the screen we need to change the x position. We’ll do this with a simple statement that increases or decreases the x value appropriately (this will move it across the screen and then back again).

There is one additional value we need to determine: the speed of the animation. If we simply increment the value by 1, the smiley face will only move one pixel per iteration. We want to put a little bit of pep in this animation, so we will create a new variable called speed and set it to 6. When this number is added to the current x position, it will move the smiley face forward or back six pixels, thus increasing the speed. Let’s look at the code:

var speed = 6; //px it moves on each loop determines how fast it moves

 x += speed;

if(x <= 0 || x >= 600){ //as far as we can go without cutting off
    speed = -speed;  //determines if it moves forwards or backwards;
}

Implementing requestAnimationFrame

As mentioned earlier, requestAnimationFrame is a new specification in the HTML5 family. It’s so new that most browsers only support a prefixed version of it. In order to utilize it in modern browsers, we need to do a quick check to see which version of the method we need to use, and then build a reference to it.

We will use the requestAnimationFrame method in our example to iterate through our animation. To accomplish this, we will use it to call the same draw method cyclically. Remember, the frame rate will be determined by requestAnimationFrame, as it will call the draw method as soon as the browser is ready to draw another screen.

Putting It All Together

The requestAnimationFrame method is really the glue that holds this example together. To get everything working properly, we will set our variables at the top of our page and then break our code into two methods. The first will determine the new x value and then call the draw method.

The draw method will first clear the canvas from the previous frame and then draw out the new frame. This method gets called over and over again. Our final code assembles into this:

var x =  0;
var speed = 6; //px it moves on loop determines how fast it moves
var canvas  = document.getElementById("moveSmile");
var smileCtx = canvas.getContext("2d");

function animate(){

   reqAnimFrame = window.mozRequestAnimationFrame||window.webkitRequestAnima
tionFrame
 ||window.msRequestAnimationFrame||window.oRequestAnimationFrame
        reqAnimFrame(animate);

        x += speed;

        if(x <= 0 || x >= 600){
          speed = -speed;  //see if it moves forwards or backwards;
        }

        draw();
    }


    function draw() {

        smileCtx6.clearRect(0, 0, 800, 200);

        smileCtx6.beginPath();

        smileCtx6.fillStyle = '#F1F42E';
        smileCtx6.arc(x+100,100,99,0,Math.PI*2); // head

        smileCtx6.stroke();
        smileCtx6.fill();

        smileCtx6.beginPath();
        smileCtx6.moveTo(x+170,100);
        smileCtx6.arc(x+100,100,70,0,Math.PI);   // Mouth
        smileCtx6.stroke();

        smileCtx6.beginPath();
        smileCtx6.fillStyle = 'black';
        smileCtx6.moveTo(x+60, 65);
        smileCtx6.arc(x+60,65,12,0,Math.PI*2);  // Left eye
        smileCtx6.fill();

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

    animate();

Figure 4-17 shows a snapshot from our example. Our smiley face starts at the far-left side of the canvas element, and then animates to the far-right side. It will then repeat this step over and over again.

A frame from the smiley face animation showing the smiley face moving from one side of the canvas element to the other and back again
Figure 4-17. A frame from the smiley face animation showing the smiley face moving from one side of the canvas element to the other and back again

Hack 42. Build “Native” Illustrations with Scalable Vector Graphics

Scalable Vector Graphics (SVG) is usually the most “familiar” graphics format in the HTML5 family of technologies. This hack will quickly get you working with the SVG format as though it were part of the DOM (hint: it really is part of the DOM!).

Scalable Vector Graphics is the W3C’s recommendation for web illustrations. Similar to Flash, SVG is a markup language for describing two-dimensional vector graphics, but it’s an open XML-based language as opposed to being proprietary. Think of SVG as being the graphical equivalent to HTML, and like HTML, SVG works seamlessly with other browser technologies such as JavaScript, CSS, and the DOM.

Why SVG?

Compared to all the other graphics and media-based technologies introduced in HTML5, SVG has some major advantages. The primary advantage is the technology itself. Being an XML-based language, SVG doesn’t require an editing program like Flash, Photoshop, or even Paint. You can create and edit SVG images with any simple text editor, or with your favorite web editor. The S in SVG stands for Scalable, and scalable it is! SVG is resolution-independent. Your SVG images can be zoomed in or out, and even printed at any size, and they will still maintain their quality, which is the primary benefit of the technology.

Being pure XML, SVG is natively searchable, indexable, and easily compressible. It’s also quite natural to embed text within your SVG files and then style them with CSS. It’s also easy to make SVG graphics compliant with the Americans with Disabilities Act (ADA), by embedding descriptions of the images within the SVG file itself.

Creating Your SVG Image

In most cases SVG is managed in its own file. This is a text-based file ending with .svg. You would then embed that file into the DOM in a manner similar to how you would work with an image. In our example, we’ll start with a new SVG file named smiley.svg and embed it into our sample page with the following code:

<object data="smiley.svg" type="image/svg+xml" />

Technically, our SVG file is an object on the page, not an image, and therefore is embedded with an object file. At this point we will see our object in the DOM, but it will not display anything, as the SVG file is blank. But we will fix that.

Now, to really impress our friends and enemies we’ll build an SVG object that demonstrates the cross-cultural symbol for love, peace, and hope: the smiley face.

Drawing with XML

Unlike a JPEG or PNG image, where the image is transmitted in Unicode, an SVG image is drawn out by a series of rules that follow the XML schema. This tends to make the images lightweight and ultra-scalable. In the preceding code example, we created an object element that has a data attribute pointing to an SVG file. This SVG file contains a few lines of code that draw out our smiley face. Before we start, let’s see how our end product will look (see Figure 4-18).

The smiley face SVG image represented in our sample web page template
Figure 4-18. The smiley face SVG image represented in our sample web page template

This cheeky smiley face is truly simple. The SVG file consists of only five elements, and each element becomes a discrete DOM element once it’s imported into the page. As DOM elements, they follow all the same rules and have access to the same APIs as all other page elements. Let’s take a quick look at each element comprising our smiley face:

 <circle cx="300" cy="164" r="160" fill="yellow" stroke="black"
stroke-width="2" />

This first element is the yellow circle that represents the head of the smiley face. As you can glean from the preceding code, the element is actually a circle element that has attributes representing the following:

cx, cy

These are the x and y positions of the circle as it relates to the SVG object in the page.

r

This is the radius of the circle represented in points (a numeric value).

fill

This refers to how the inside of the object is painted. An element can be filled with a color, a gradient, or a pattern (such as an imported image).

stroke

This represents the actual shape of the object or line (including the text). The stroke can be colored with the same options as the fill.

stroke-width

This is only necessary when you have a stroke declared. As is obvious in the attribute name, this declares the width of the stroke in points. The default is 1.

The next two elements are the eyes of the smiley face. They contain many of the same attributes as the previous circle. The two circles are identical to each other except for the x positions that draw them on different sides of the head.

  <circle cx="210" cy="100" r="20" fill="black" />

  <circle cx="380" cy="100" r="20" fill="black" />

The clip path may be an unexpected element for this illustration. Not to give away the ending, but the final element will be another circle that represents the smiling mouth in the illustration.

  <clipPath id="MyClip">
    <rect x="30" y="200" width="600" height="100" />
  </clipPath>

The clip path is a parent element to other SVG elements that have a clipping effect instead of a painting effect. The clip path has a single attribute:

id

The id looks a lot like a DOM id because it is a DOM id, and it’s necessary in this case for us to reference the clip path by another SVG element.

The clip path contains another element:

    <rect x="30" y="200" width="600" height="100" />

This is exactly what it looks like. You were already introduced to the circle element; well, this is a rectangle element. If the rect element wasn’t contained by a clip path, it would draw a 600 × 100 rectangle on the SVG object.

  <circle cx="300" cy="160" r="120" fill-opacity="0" stroke="black"
  stroke-width="5" clip-path="url(#MyClip)" />

This final object is the mouth. This circle has two new attributes that we want to look at:

fill-opacity

This is a value between 0 and 1 that declares how opaque our fill should be. Since we want the circle to appear empty, we have it set to 0.

clip-path

This references the DOM id for the clip path within our SGV file. When we reference the clip path, the shape gets applied to this element, in a manner that clips off anything (fill or stroke) within the image.

We could have drawn a line for the mouth of the smiley face, but it would have been a lot more work to draw out that shape in XML than it would be to just declare a whole circle, and then clip half of it off.

When we take all those SVG elements and wrap them in an <svg> tag, we can see how simple the code really is:

<svg version="1.1"
     baseProfile="full"
     xmlns="http://www.w3.org/2000/svg">
  <circle cx="300" cy="164" r="160" fill="yellow" stroke="black"
   stroke-width="2" />
  <circle cx="210" cy="100" r="20" fill="black" />
  <circle cx="380" cy="100" r="20" fill="black" />
  <clipPath id="MyClip">
    <rect x="30" y="200" width="600" height="100" />
  </clipPath>
  <circle cx="300" cy="160" r="120" fill-opacity="0" stroke="black"
   stroke-width="5" clip-path="url(#MyClip)" />
</svg>

These 11 lines of code are all we need to draw out our friendly smiley face. The code is significantly lighter than a JPEG or even a GIF that would represent the same image. Additionally, you inherit all the benefits of first-class DOM objects, as we previously discussed.

Hack 43. Style SVG Elements with CSS

SVG has the same privileges as all other DOM elements, including the ability to be styled with CSS. This hack demonstrates how easy it is to create elements with SVG, and then turn them into illustrations with CSS.

The most powerful part of SVG is its standing in the DOM. SVG elements are first-class elements in HTML5, and they have every privilege that other DOM elements have. That being said, it’s simple to control the presentation of these elements with CSS.

For a refresher on how to implement SGV, see [Hack #42], which discusses how to create SVG elements.

SVG has the ability to control the presentation of its elements by setting attributes on the elements themselves. Here are some of the more popular presentation attributes in SVG:

  • fill

  • stroke

  • stroke-width

  • fill-opacity

  • height

  • width

  • x, y

  • cx, cy

  • orientation

  • color

  • cursor

  • clipPath

Many times it makes sense to embed these attributes within the SVG itself where it can be downloaded as one file. Other times it may be more flexible to create our base SVG elements within the SVG file, and style them with a language we are very familiar with: CSS.

Starting with SVG

To get started we will illustrate a simple smiley face with SVG elements. This is a basic illustration that consists of five elements, and nine lines of code:

<svg version="1.1"
     baseProfile="full"
     xmlns="http://www.w3.org/2000/svg">
  <circle cx="300" cy="164" r="160" fill="yellow" stroke="black"
   stroke-width="2" />
  <circle cx="210" cy="100" r="20" fill="black" />
  <circle cx="380" cy="100" r="20" fill="black" />
  <clipPath id="MyClip">
    <rect x="30" y="200" width="600" height="100" />
  </clipPath>
  <circle cx="300" cy="160" r="120" fill-opacity="0" stroke="black"
   stroke-width="5" clip-path="url(#MyClip)" />
</svg>

This simple code snippet gets stored in a file called smiley.svg and embedded into our page with an <object> tag as follows:

<object data="smiley.svg" type="image/svg+xml" />

Once the object is on the page, we see the SVG image as it should appear (see Figure 4-19).

SVG representation of a smiley face
Figure 4-19. SVG representation of a smiley face

Stripping Away the Noise

In order to move our visual aspects of the illustration to CSS, we need to strip all the visual aspects out of our SVG. We basically want to leave ourselves with some raw shapes that we can manipulate. We will do this by removing most of the attributes from the SVG file. The one attribute we will not remove is the circle radius, as there is no CSS equivalent to this. Here is what our plain Jane SVG will look like:

<svg version="1.1"
     baseProfile="full"
     xmlns="http://www.w3.org/2000/svg">
  <circle r="160" class="head"  />
  <circle r="20" class="eye leftEye" />
  <circle r="20" class="eye rightEye" />
  <clipPath id="MyClip">
    <rect class="clipBox" width="100%" height="100%" />
  </clipPath>
  <circle r="120" class="mouth"  />

I want to point out a few things about the preceding SVG code. First, note the rect element with the class of clipBox. We have inserted a width and height of 100%. At the time of this writing, current implementations of clip boxes require some height and width attributes set in the element to take effect. Second, I have added a class attribute to each element and assigned at least one class name to each element. Although I could have assigned all the CSS via pseudotags based on DOM position, I prefer to use class names, as they’re more flexible if the DOM should change.

Since our elements have no look and feel to them, we end up with an SVG element that looks like Figure 4-20.

Our SVG components as they appear unstyled
Figure 4-20. Our SVG components as they appear unstyled

Our SVG elements have no visual characteristics or positioning, so we’re starting with a series of circles stacked on top of the SVG object. Think of your SVG object as being like an iframe (an HTML element that loads a new page inside your current page), having its own separate DOM. As an aside, if you don’t want to use an <object> tag to create your element on the page, you can use an iframe to create SVG elements on the page as well.

In the preceding example we have a few simple class names, such as head and eye. If we write CSS declarations based on these class names and put them in our master stylesheet, they won’t actually affect our SVG elements, as the CSS will not cascade down to the SVG elements. To resolve this issue we need to load our CSS in one of three ways. The first way is with inline CSS where we put a style attribute on the element itself and set our styles directly in the element:

<circle class="head"  r="160"  style= fill: yellow; stroke: black;
stroke-width: 2px; "/>

The second way is by attaching a class name to the element (as we have) and then referencing it in an embedded style block. It’s important to remember that the style block needs to be directly in the SVG file, not in the page DOM:

<circle class="head"  r="160" />
<style>
.head {fill: yellow; stroke: black; stroke-width: 2px;}
</style>

The third method, and our choice for this hack, is to attach a class name to the element and then reference an external stylesheet. Again, it’s important to reference the stylesheet from the SVG file and not from the HTML page file. There is also a bit of a twist on this stylesheet. SVG is XML-based but it isn’t HTML, so our traditional link reference will not work properly within the SVG file. For external CSS, the SVG specification references an ancient specification on referencing a stylesheet within an XML document. My only assumption was that this specification was part of the Dead Sea Scrolls discovery or something. You can find the old specification at w3.org.

According to this specification, the stylesheet is loaded with a tag at the top of the SVG file, like so:

<?xml-stylesheet type="text/css" href="/assets/css/svg.css"?>

This, of course, will have an href that will point to the location of your stylesheet on the server, so yours may look different from this example.

Building the CSS

Now that our structure is in place, let’s dig into the CSS that we will use to return our little smiley face to its full glory. We basically have two factors to deal with for each element. The first is the visual attributes, and the second is the position. The visual attributes are quite simple: you will see in our CSS that we have basically taken our old inline attributes of stroke, fill, stroke size, and the like and set them in CSS. Here is a sample from our CSS:

  .head{
        fill: yellow;
        stroke: black;
        stroke-width: 2px;
        }

    .mouth {
        stroke: black;
        fill-opacity: 0;
        stroke-width: 5px;
    }

That was simple enough. The second factor to address is positioning. In order to not have all our elements stacked up on top of one another, we need to tell them where to go. For this, we will pull out one of our new CSS3 attributes called transform, which we will use to move our elements into place. Here is a sample of our CSS3 transforms within our CSS:

    .eye {
       transform:translate(210px, 100px);
        }

    .rightEye {
        transform:translate(380px, 100px);
        }

The transform specifies the translate (or repositioning) of each element from its current position, which again is with the radius centered at (0,0) or the top-left corner of the SVG element.

Each element has CSS specified to provide the visual attributes and the positioning. When we put it all together, our CSS file contains the following declarations:

    .head{
        fill: yellow;
        stroke: black;
        stroke-width: 2px;
      transform:translate(300px, 164px);
        }
    .eye {
        fill: black;
       transform:translate(210px, 100px);
        }

    .rightEye {
        transform:translate(380px, 100px);
        }

    .mouth {
        stroke: black;
        fill-opacity: 0;
        stroke-width: 5px;
        clip-path: url(#MyClip);
       transform:translate(0px, 0px);


    }
    .clipBox {
        width: 600px;
        height: 100px;
      transform:translate(30px, 200px);

    }

For more information on how the clip path works, see [Hack #42] where we clarify how and why we use clip paths.

With this CSS, our finished product looks identical to the one we started with where all our attributes were directly within the SVG elements. Figure 4-21 shows our finished product.

The SVG element using CSS for styling
Figure 4-21. The SVG element using CSS for styling

Hack 44. Animate Illustrations with SVG

Easily turn your SVG illustrations into SVG animations by adding a few lines of HTML or CSS. That’s right, no JavaScript is necessary for this easy animation.

Before HTML5, animation was cumbersome. It was never intended to be done on the Web, which might be why developers worked so hard to make it happen. Before HTML5, all animation had to be done with JavaScript. It took us back to the days of stop-frame animation where we had to move the object being animated one frame at a time. With JavaScript, we would slowly change the attribute we were trying to animate one or two pixels at a time. Whether it was height (to make a window slide open) or position (to animate something across the screen), JavaScript would repetitively alter the style attributes until the “animation” was complete. As you can imagine, it wasn’t only code-heavy, but processor-heavy as well.

Along comes SVG, bringing with it some easy-to-perform, hardware-accelerated animations. In this hack we’ll look at two animation options in our SVG tool belt.

The SVG <animateMotion> Tag

SVG is completely XML-based. So it only makes sense that it has a tag for animation. Let’s start with a simple box and bouncing ball. This requires only a few lines of SVG:

<svg version="1.1"
     baseProfile="full"
     xmlns="http://www.w3.org/2000/svg">

    <rect x="100" y="0" width="400" height="100"  fill="pink"
     stroke="black" stroke-width="1" />
    <circle cx="120" cy="50" r="20" fill="blue" stroke="black"
     stroke-width="1" />
  </svg>

From this code we end up with a rectangle with a circle inside it (see Figure 4-22).

SVG elements without any animation
Figure 4-22. SVG elements without any animation

In order to animate this ball moving from one side of the rectangle to the other, we will add a new tag and nest it inside the circle element, as a child element (think of it as a command associated with the circle element). Let’s look at our new SVG and then we will walk through the details of the new tag:

<svg version="1.1"
     baseProfile="full"
     xmlns="http://www.w3.org/2000/svg">

    <rect x="100" y="0" width="400" height="100"  fill="pink"
    stroke="black" stroke-width="1" />
    <circle cx="120" cy="50" r="20" fill="blue" stroke="black"
    stroke-width="1">
      <animateMotion path="M 0 0 H 380 Z" dur="3s"
      repeatCount="indefinite" />
    </circle>
  </svg>

This new animateMotion tag allows us to animate the circle element while all other elements stay fixed. In this tag we are utilizing three attributes:

path

The path is the hardest part of this tag. It appears to be a random list of numbers and letters that somehow give us a perfect path from one end of the rectangle to the other. This path is actually a wrap-up of our motion command. Breaking it down, the M represents the command to “move to” a new location, the 0 0 is the x,y start position, and H tells it to move horizontally. From there, 380 is the distance it should move measured in points, and the Z command closes the path and tells it to start back at the beginning. This notation is all part of the SMIL (Synchronized Multimedia Integration Language) Specification, the details of which you can access on the W3C website.

dur (duration)

This attribute defines how long it will take to complete a full path. Values are represented in seconds with the format of 3s.

repeatCount

This attribute defines how many times the path will “repeat.” Don’t be fooled by the word repeat; a value of 1 will run the path only once, and a value of 5 will run the path five times. In our case, we set it to indefinite, so it will run until the page is closed or the value is changed.

Our ball will now bounce back and forth within the rectangle. With SVG, animation is at the root of the language. Just as any other component becomes a value in the DOM, so does our <animation> tag, and it can be accessed and altered with JavaScript. Figure 4-23 shows a view of our end product.

SVG animating the ball back and forth inside the box with only one line of code
Figure 4-23. SVG animating the ball back and forth inside the box with only one line of code

Flexibility in Structure

In our first example, we made the <animation> tag a child tag to the element that was being animated. In many cases you may have a group of tags that you want to animate. To address such situations we will pull out some code from a previous hack of our smiley face created in SVG. If we want to animate this smiley face back and forth on the screen, we certainly don’t want to have to animate each element separately. This would be both time-consuming to code and intensive on our processor, as the engine would be calculating each element separately. Let’s look at two different code samples showing how to animate a group of SVG tags together.

Here is our first sample:

<svg version="1.1"
     baseProfile="full"
     xmlns="http://www.w3.org/2000/svg">
  <g>
  <circle cx="300" cy="164" r="160" fill="yellow" stroke="black"
  stroke-width="2" />
  <circle cx="210" cy="100" r="20" fill="black" />
  <circle cx="380" cy="100" r="20" fill="black" />
  <clipPath id="MyClip">
    <rect x="30" y="200" width="600" height="100" />
  </clipPath>
  <circle cx="300" cy="160" r="120" fill-opacity="0" stroke="black"
  stroke-width="5" clip-path="url(#MyClip)" />
  <animateMotion path="M 0 0 H 300 Z" dur="3s"
  repeatCount="indefinite"></animateMotion>
  </g>
</svg>

Here is our second sample:

<svg version="1.1"
     baseProfile="full"
     xmlns="http://www.w3.org/2000/svg">
  <animateMotion path="M 0 0 H 300 Z" dur="3s" repeatCount="indefinite">
  <circle cx="300" cy="164" r="160" fill="yellow" stroke="black"
  stroke-width="2" />
  <circle cx="210" cy="100" r="20" fill="black" />
  <circle cx="380" cy="100" r="20" fill="black" />
  <clipPath id="MyClip">
    <rect x="30" y="200" width="600" height="100" />
  </clipPath>
  <circle cx="300" cy="160" r="120" fill-opacity="0" stroke="black"
  stroke-width="5" clip-path="url(#MyClip)" />
  </animateMotion>

</svg>

In the first sample we had a parent element to our code, named g, which is code for “group”. Once we group our code together, it’s treated as one element (on that level) and our <animateMotion> tag simply becomes another child tag to the g element whose job is to animate the group of elements.

In the second sample, instead of introducing the new tag we simply use the <animateMotion> tag as a parent to enclose the tags that produce the smiley face. The <animateMotion> parent tag animates the tags nested inside it as a group, and the process is streamlined significantly as compared to animating each element individually.

One Last Option

Don’t you just love SVG? Just like HTML, there is always more than one way to accomplish everything. This flexibility allows you to pick the method that works best in your particular situation. With SVG animation, there is no shortage of options.

Keeping in mind that SVG elements become DOM elements just like any other HTML page elements, we can animate our SVG just as we would HTML, by using CSS. In the preceding sample that introduced the g element, we can remove the <animateMotion> tag completely, and set an id on the g element. From here, we can use a CSS3 transform to create the same animation. For more on applying CSS to SVG elements, see [Hack #43].

Hack 45. Embed SVG Directly in Your HTML

You can embed SVG directly within your HTML file, negating the need for an external .svg file. With HTML5, your SVG elements can live in the same DOM as your HTML, and you’ll be removing some of the barriers of managing the two code bases separately.

SVG is powerful and can be quite complex, creating limitless illustrations and animations with a simple XML-based language. But in some cases you may only have a simple illustration that doesn’t require the rigor of an external file to manage the code. Just as HTML5 provides the ability to inline images directly in your markup, SVG can be embedded directly within your HTML as well.

Looking at the code that is involved, you can see that it’s exactly what you’d expect it to be. We have our HTML page, and instead of using an <object> tag that points to an external SVG file, we see the entire content of the previously external SVG file directly within our HTML. In our example, we will use our trusty old smiley face SVG illustration embedded directly within our HTML:

<doctype !html>
<html>
<head>
<meta charset="utf-8">
    <title>SVG Sample</title>
<link href="assets/css/bootstrap.css" rel="stylesheet" />
<link href="assets/css/bootstrap-responsive.css" rel="stylesheet" />
<head>
<body>
<div class="navbar...  ...</div>
<h1> My Inline SVG Sample</h1>
<div id="svgWrapper" class="row">
<svg version="1.1"
     baseProfile="full"
     xmlns="http://www.w3.org/2000/svg">
  <circle cx="300" cy="164" r="160" fill="yellow" stroke="black"
  stroke-width="2" />
  <circle cx="210" cy="100" r="20" fill="black" />
  <circle cx="380" cy="100" r="20" fill="black" />
  <clipPath id="MyClip">
    <rect x="30" y="200" width="600" height="100" />
  </clipPath>
  <circle cx="300" cy="160" r="120" fill-opacity="0" stroke="black"
  stroke-width="5" clip-path="url(#MyClip)" />

</svg>

</div>

</body>
</html>

In the preceding example, once the renderer sees the SVG declaration tag it switches parsers from HTML to SVG. When the tag closes it goes back from SVG to HTML. Our results look exactly as they did when the SVG was in an external file (see Figure 4-24).

The SVG smiley face illustration where the SVG is inline with the HTML
Figure 4-24. The SVG smiley face illustration where the SVG is inline with the HTML

Why Inline?

Aside from the obvious “ease of use” argument, there are additional benefits to putting your SVG inline. Above all, inline SVG can be used as a performance enhancement. In some scenarios, you may benefit from not having to load the external file (as it will require an additional call to your server) and can put the SVG code directly in the page. Keep in mind that this is not always a performance enhancement, especially when you have a large amount of SVG code, which your browser may be able to load in parallel with your elements to save time. In cases like ours, when your illustration consists of only a few lines of code, it will generally be better in terms of performance to inline it and remove the additional call.

An additional benefit of inline SVG can be for DOM reference. In general, your SVG file is a separate DOM from your page when it’s loaded in an external SVG file (think of it as an iframe, an HTML element that loads a separate page inside the element). That being the case, any CSS that affects your SGV must be placed in or linked from your SVG file, and therefore can’t be applied to the HTML or the page. JavaScript similarly needs to access the SVG elements through the SVG object and falls prey to the same limitations as accessing items in an iframe. Moving the SVG into the DOM directly removes those barriers and allows you to truly treat your SVG elements just as any other DOM element.

Let’s look at a quick and quirky example of how inline SVG is affected by CSS declarations. In our example, we have a simple CSS declaration:

<style>
circle {
stroke: red;
 stroke-width: 12px;
}
 </style>

This style block is embedded directly into our HTML page, and in our page we have two SVG smiley faces. The first face is loaded as an external SVG image (Figure 4-25), and the second is loaded as an inline SVG image (Figure 4-26).

An SVG smiley face, loaded as an external .svg file
Figure 4-25. An SVG smiley face, loaded as an external .svg file
An SVG smiley face, loaded inline in the HTML document
Figure 4-26. An SVG smiley face, loaded inline in the HTML document

As you can see, the CSS applies to none of the circles within our embedded SVG and to every circle within our inline SVG. Embedded SVG may not always be the best choice for your documents, but it’s always nice to have options.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required