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 39. Style Canvas Elements with ...

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.

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