17

Is it possible to 'fill' a shape on an HTML5 canvas with an image instead of a color?

I've drawn a bunch of shapes (squares with various corners sliced off at 45 degree angles). I'd like to be able to 'fill' these shapes with an image, instead of a color. At the moment I've got a line stating:

context.fillStyle = '#123456' // example fill color

What I'm looking for is something like:

context.fillStyle = 'url(http://www.myimagereference.com/image.png)';

I know that I can't use fillStyle this way - but is there another way to achieve this kind of thing?

Charlie
  • 173
  • 1
  • 1
  • 6

2 Answers2

16

You might wanna have a look at createPattern
below is a simple code which demonstrates the use of createPattern

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var w = canvas.width = 256;
var h = canvas.height = 256;
var img = new Image();

img.src = "http://www.gravatar.com/avatar/e555bd971bc2f4910893cd5b785c30ff?s=128&d=identicon&r=PG";
img.onload = function () {
    var pattern = ctx.createPattern(img, "repeat");
    ctx.fillStyle = pattern;
    ctx.fillRect(0, 0, w, h);
};

Try an Example

  • 2
    Note that `createPattern` does not (as far as I can tell) allow you to scale the image, either proportionately or otherwise. This may be fine, depending on the use case. If you need stretching, you could either use my answer, or draw the image stretched to an offscreen canvas and use that canvas to create the pattern. – Phrogz Feb 10 '11 at 21:08
12

You can do this by defining a clipping region that is the same as your shape and then using drawImage() to draw into this region; then stroke (only) your path on top of this.

I've created an example of this technique for you on my website:
http://phrogz.net/tmp/canvas_image_as_background_to_shape.html

Here's the relevant code; it proportionately scales the image to fill the width you specify:

function clippedBackgroundImage( ctx, img, w, h ){
  ctx.save(); // Save the context before clipping
  ctx.clip(); // Clip to whatever path is on the context

  var imgHeight = w / img.width * img.height;
  if (imgHeight < h){
    ctx.fillStyle = '#000';
    ctx.fill();
  }
  ctx.drawImage(img,0,0,w,imgHeight);

  ctx.restore(); // Get rid of the clipping region
}

It's up to you to modify that if you want tiling, or asymmetric stretching, low-opacity tinting, etc. Here's how you might use it:

function slashedRectWithBG( ctx, x, y, w, h, slash, img ){
  ctx.save(); // Save the context before we muck up its properties
  ctx.translate(x,y);
  ctx.beginPath();
  ctx.moveTo( slash, 0 );       //////////// 
  ctx.lineTo( w, 0 );          //         //
  ctx.lineTo( w, h-slash );   //          //
  ctx.lineTo( w-slash,h );    //          //
  ctx.lineTo( 0, h );         //         //
  ctx.lineTo( 0, slash );     ////////////
  ctx.closePath();
  clippedBackgroundImage( ctx, img, w, h );
  ctx.stroke();  // Now draw our path
  ctx.restore(); // Put the canvas back how it was before we started
}

Note that when you create your image to pass to the function, you must set its onload handler before setting the src:

var img = new Image;
img.onload = function(){
  // Now you can pass the `img` object to various functions
};
img.src = "...";
Community
  • 1
  • 1
Phrogz
  • 296,393
  • 112
  • 651
  • 745