How would I fillRect with an image?

17,683

Solution 1

Here is a quick example of how you can use drawImage to draw an image to a canvas. The element on the left is the image, the element on the right is the canvas with the image drawn on it.

JSFiddle: https://jsfiddle.net/gw8ncg7g/

window.onload = function() {
    var c = document.getElementById("myCanvas");
    var ctx = c.getContext("2d");
    var img = document.getElementById("image");
    ctx.drawImage(img, 0, 0);
}
canvas {
    border:1px solid #d3d3d3;
}
<img id="image" width="300" height="300" src="http://i.imgur.com/LDR6AWn.png?1">
<canvas id="myCanvas" width="300" height="300" >

Solution 2

The question is ambiguous as there are many ways to "fillRect with an image".

First off images in the browser are downloaded asynchronously so you need to wait for an image to load before you can use it. For canvas situations the most common way to get an image is to create a new Image and set an onload listener

const img = new Image();
img.onload = someFunctionToCallWhenTheImageHasLoaded
img.src = 'http://url/to/image';

Then the question is what do mean by "fillRect"

Using this 256x256 image

For example to draw the image at the size it was downloaded you can use drawImage with 3 arguments

ctx.drawImage(img, x, y);

enter image description here

const img = new Image();
img.onload = draw;
img.src = 'https://i.imgur.com/ZKMnXce.png';

function draw() {
  const ctx = document.querySelector('canvas').getContext('2d');
  ctx.drawImage(img, 0, 0);
}
canvas { border: 1px solid black; }
<canvas></canvas>

To draw the image at a different size you can use

ctx.drawImage(img, x, y, width, height);

enter image description here

const img = new Image();
img.onload = draw;
img.src = 'https://i.imgur.com/ZKMnXce.png';

function draw() {
  const ctx = document.querySelector('canvas').getContext('2d');
  const destX = 10;
  const destY = 20;
  const destWidth = 30;
  const destHeight = 40;
  ctx.drawImage(img, destX, destY, destWidth, destHeight);
}
canvas { border: 1px solid black; }
<canvas></canvas>

To draw part of the image you can use

// part of image to draw
const srcX = 10;
const srcY = 20;
const srcWidth = 130;
const srcHeight = 140;

// where to draw it
const dstX = 60;
const dstY = 70;
const dstWidth = 160;
const dstHeight = 40;

ctx.drawImage(img, srcX, srcY, srcWidth, srcHeight,
              dstX, dstY, dstWidth, dstHeight);

enter image description here

const img = new Image();
img.onload = draw;
img.src = 'https://i.imgur.com/ZKMnXce.png';

function draw() {
  const ctx = document.querySelector('canvas').getContext('2d');
  
  // part of image to draw
  const srcX = 10;
  const srcY = 20;
  const srcWidth = 130;
  const srcHeight = 140;

  // where to draw it
  const dstX = 60;
  const dstY = 70;
  const dstWidth = 160;
  const dstHeight = 40;

  ctx.drawImage(img, srcX, srcY, srcWidth, srcHeight,
                dstX, dstY, dstWidth, dstHeight);
}
canvas { border: 1px solid black; }
<canvas></canvas>

That said, "fillRect" being ambiguous maybe you wanted to use the image as a pattern in which case you need to make a pattern out of it using createPattern

const pattern = ctx.createPatttern(img, 'repeat');

For these let's use this 16x16 pixel image

You can then use the pattern as your fillStyle as in

ctx.fillStyle = pattern;
ctx.fillRect(10, 20, 30, 40);

enter image description here

const img = new Image();
img.onload = draw;
img.src = 'https://i.imgur.com/fqgm8uh.png';

function draw() {
  const ctx = document.querySelector('canvas').getContext('2d');
  
  const pattern = ctx.createPattern(img, 'repeat');
  
  ctx.fillStyle = pattern;
  ctx.fillRect(10, 20, 30, 40);
}
canvas { border: 1px solid black; }
<canvas></canvas>

Patterns are relative to the origin of the canvas which means if you just use ctx.fillRect (or any other fill) the pattern will match across fills.

ctx.fillRect(10, 20, 30, 40);
ctx.beginPath();
ctx.arc(50, 60, 25, 0, Math.PI * 2);
ctx.fill();

enter image description here

const img = new Image();
img.onload = draw;
img.src = 'https://i.imgur.com/fqgm8uh.png';

function draw() {
  const ctx = document.querySelector('canvas').getContext('2d');
  
  const pattern = ctx.createPattern(img, 'repeat');
  
  ctx.fillStyle = pattern;
  ctx.fillRect(10, 20, 30, 40);
  ctx.beginPath();
  ctx.arc(50, 60, 25, 0, Math.PI * 2);
  ctx.fill();
}
canvas { border: 1px solid black; }
<canvas></canvas>

Because patterns are anchored at the origin if you are animating without changing the origin you'll notice the pattern doesn't move

const img = new Image();
img.onload = start;
img.src = 'https://i.imgur.com/fqgm8uh.png';

function start() {
  const ctx = document.querySelector('canvas').getContext('2d');
  
  const pattern = ctx.createPattern(img, 'repeat');

  function render(time) {
    time *= 0.001;  // seconds;
    
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
    
    const x = Math.sin(time * 1.1) * 150 + 150;
    const y = Math.sin(time * 1.2) * 50 + 50;
    
    ctx.fillStyle = pattern;
    ctx.fillRect(x, y, 30, 40);
    ctx.beginPath();
    ctx.arc(x, y, 25, 0, Math.PI * 2);
    ctx.fill();
    
    requestAnimationFrame(render);
  }
  requestAnimationFrame(render);
}
canvas { border: 1px solid black; }
<canvas></canvas>

In order to move the pattern you need to move the origin of the canvas using ctx.translate (as well as ctx.rotate, ctx.scale, ctx.setTransform)

const img = new Image();
img.onload = start;
img.src = 'https://i.imgur.com/fqgm8uh.png';

function start() {
  const ctx = document.querySelector('canvas').getContext('2d');
  
  const pattern = ctx.createPattern(img, 'repeat');

  function render(time) {
    time *= 0.001;  // seconds;
    
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
    
    const x = Math.sin(time * 1.1) * 150 + 150;
    const y = Math.sin(time * 1.2) * 50 + 50;
    
    ctx.translate(x, y);
    
    ctx.fillStyle = pattern;
    ctx.fillRect(0, 0, 30, 40);
    ctx.beginPath();
    ctx.arc(0, 0, 25, 0, Math.PI * 2);
    ctx.fill();
    
    ctx.setTransform(1, 0, 0, 1, 0, 0);  // set it back to the default
    
    requestAnimationFrame(render);
  }
  requestAnimationFrame(render);
}
canvas { border: 1px solid black; }
<canvas></canvas>

Solution 3

Here's an illustration of some of the possibilities:

var im = new Image();
im.src = "https://upload.wikimedia.org/wikipedia/commons/7/79/Face-smile.svg";
im.onload = function () { /* first, wait until the image is loaded */
    /* create five canvases, and draw something in each */
    for (var i=1; i<=5; i++) {
    var canvas = document.createElement("canvas");
    document.body.appendChild(canvas);
    canvas.width = canvas.height = 200;
    var ctx=canvas.getContext("2d");
    var x=50, y=50; /* where to plot */
    var w=20, h=60; /* width and height of rectangle, if applicable */
    switch (i) {
    case 1:
        /* first canvas: draw a rectangle */
        ctx.fillRect(x, y, w, h);
        break;
    case 2:
        /* second canvas: draw an image, actual size, no clipping */
        /* coordinates are where the top left of the image is plotted */
        ctx.drawImage(im, x, y);
        break;
    case 3:
        /* third canvas: draw an image, scaled to rectangle */
        ctx.drawImage(im, x, y, w, h);
        break;
    case 4:
        /* fourth canvas: draw an image, actual size, clipped to rectangle */
        ctx.save();
        ctx.rect(x, y, w, h);
        ctx.clip();
        ctx.drawImage(im, x, y);
        ctx.restore();
        break;
    case 5:
        /* fifth canvas: draw shapes filled with a background image */
        ctx.fillStyle = ctx.createPattern(im, 'repeat'); /* or 'no-repeat', or 'repeat-x', or 'repeat-y' */
        /* note that the image is tiled from the top left of the canvas */
        ctx.fillRect(x, y, w, h);

        /* also draw a circle, why not */
        ctx.beginPath();
        ctx.arc(150, 150, 40, 0, Math.PI*2);
        ctx.fill();
        break;
    }
    }
}
im.onerror = function() { alert("failed to load image"); };

Jsfiddle: http://jsfiddle.net/efeqjjno/

Share:
17,683
OneStig
Author by

OneStig

Updated on June 27, 2022

Comments

  • OneStig
    OneStig almost 2 years

    Normally you can fill a rectangle in a canvas withctx.fillStyle = "whatever color here" and then ctx.fillRect(cords and length and width here). Is there a syntax where I can say ctx.fillRect(someImagePathHere, xOfTopLeft, yofTopLeft)

    If not, how else can I achieve this?

  • Maximillian Laumeister
    Maximillian Laumeister over 8 years
    The syntax is drawImage(image, dx, dy), where dx and dy are the coordinates on the canvas of the top left corner of the image. drawImage also supports drawing the image scaled - see more details here: developer.mozilla.org/en-US/docs/Web/API/…
  • iamtravisw
    iamtravisw about 4 years
    This answer was very educational, and it helped me understand the question much better than the selected answer. Thank you for taking the time to type all of this out!