Syn
Syn

Reputation: 1128

js canvas create pattern repeat issues

Currently drawing this onto a canvas:

enter image description here

I want to use a texture that repeats only on the Y axis where the red rectangle is.

However, when drawing the texture, nothing shows up when repeat-y is specified. It works when the repeat value is repeat however.

ctx.fillStyle = ctx.createPattern(assets.grassRightTexture, 'repeat-y');
ctx.fillRect(dimensions.left + dimensions.width, dimensions.top, 5, dimensions.height);

The assets.grassRightTexture is a preloaded img element with a 4px by 32px sprite.

Not sure if im doing something wrong but I didnt notice anything when reading the moz canvas docs.

Thank you.

Upvotes: 1

Views: 138

Answers (1)

Kaiido
Kaiido

Reputation: 136627

The CanvasPattern object is always relative to the context's current transformation matrix (CTM) and not to the shape where you expect it to be drawn.

So here, since you do create your pattern from an <img> element that has its width set to 4px, your CanvasPattern instance will cover only the rectangle from the coordinates (0, -∞) to (4, ∞).
Since the rectangle your draw doesn't cover this area, you're filling with transparent pixels.

const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
// prepare the CanvasPattern
canvas.width = 4;
canvas.height = 32;
ctx.fillStyle = "red";
ctx.fillRect(0, 0, canvas.width, canvas.height);
const pattern = ctx.createPattern(canvas, "repeat-y");
// try to use the pattern
canvas.width = 300;
canvas.height = 150;
ctx.fillStyle = pattern;
ctx.fillRect(24, 50, 50, 50); // doesn't draw anything
ctx.strokeRect(24, 50, 50, 50); // to show we did draw something

setTimeout(() => {
  ctx.fillRect(0, 75, 50, 50); // doesn't draw anything
  ctx.strokeRect(0, 75, 50, 50); // to show we did draw something
}, 1000);
<canvas></canvas>

To overcome this you can either transform your CanvasPattern object thanks to its setTransform() method,

const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
// prepare the CanvasPattern
canvas.width = 4;
canvas.height = 32;
ctx.fillStyle = "red";
ctx.fillRect(0, 0, canvas.width, canvas.height);
const pattern = ctx.createPattern(canvas, "repeat-y");
// try to use the pattern
canvas.width = 300;
canvas.height = 150;
ctx.fillStyle = pattern;
// we translate the CanvasPattern so that our shape is covered
// setTransform accepts a DOMMatrixInit dictionary
// `e` represents the x-axis translate
pattern.setTransform({e: 24}); 
ctx.fillRect(24, 50, 50, 50);
ctx.strokeRect(24, 50, 50, 50);
<canvas></canvas>

or you can draw on your context in two steps with 2 different CTM for tracing the shape and for painting it.

const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
// prepare the CanvasPattern
canvas.width = 4;
canvas.height = 32;
ctx.fillStyle = "red";
ctx.fillRect(0, 0, canvas.width, canvas.height);
const pattern = ctx.createPattern(canvas, "repeat-y");
// try to use the pattern
canvas.width = 300;
canvas.height = 150;
ctx.fillStyle = pattern;
// we trace at identity CTM
ctx.rect(24, 50, 50, 50);
// we move only the fillStyle & strokeStyle
ctx.translate(24, 0);
ctx.fill();
ctx.stroke();
<canvas></canvas>

Though there is a bug in Firefox that makes this second option fail there.

Upvotes: 2

Related Questions