This a Firefox bug.
First I'd like to clear up what I fear is a misconception here. The Path2D object you created is not connected to your 2D context anyhow before you use it with ctx.fill(p); ctx.stroke(p);
.
It is only inside these methods that the coordinates of its sub-paths will get transformed to the context's current matrix.
So the ctx.translate(100,100);
has no effect on the Path2D object, it will have on both context's methods though.
Also, your Path2D is actually just p.rect(-100, -100, 200, 200)
.
So if we replace all this Path2D code to a simple ctx.rect()
call, we can see that the discrepancy you noticed is not related to the Path2D API:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.fillStyle = generatePattern();
ctx.translate(100, 100);
ctx.rect(-100, -100, 200, 200);
ctx.fill();
ctx.stroke();
function generatePattern() {
var offCanvas = document.createElement('canvas');
offCanvas.width = 200;
offCanvas.height = 200;
var offCtx = offCanvas.getContext("2d");
offCtx.fillStyle = "red";
offCtx.fillRect(0, 0, 200, 200);
return ctx.createPattern(offCanvas, "no-repeat");
}
<canvas id="canvas" width="500" height="250"></canvas>
So what happens, is that the fillStyle
(and strokeStyle
) property is like an infinitely big layer, itself relative to the context's transform matrix. When you set it to either a CanvasPattern, or a CanvasGradient, this matters since the position of your bitmap will get ruled by this transformation matrix.
Here is a simple example showing how this can be used, to create a moving gradient, while the sub-path remains the same.
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
// we define the gradient only once
ctx.fillStyle = generateGradient();
// we define the sub-path only once
ctx.lineTo(20, 20);
ctx.lineTo(190, 50);
ctx.lineTo(130, 190);
ctx.closePath();
draw({clientX:50, clientY:50});
canvas.onmousemove = draw;
function draw(evt) {
var rect = canvas.getBoundingClientRect();
var x = evt.clientX - rect.left - 50;
var y = evt.clientY - rect.top - 50;
// clear
ctx.setTransform(1,0,0,1,0,0);
ctx.clearRect(0,0,canvas.width,canvas.height);
// here, all that moves is the fillStyle layer
ctx.translate(x, y);
ctx.fill();
ctx.stroke();
}
function generateGradient() {
var grad = ctx.createRadialGradient(
50,
50,
0,
50,
50,
25
); // a circle whose center is at 50,50, and rad is 25
grad.addColorStop(0, 'red');
grad.addColorStop(1, 'green');
return grad;
}
move your mouse over the triangle
<canvas id="canvas" width="500" height="250"></canvas>
In your case, the pattern you created will get moved by 100px on both x and y axis, hence the top-left corner of your offCanvas will be rendered at pixel 100,100 and since your Path only renders until pixel 200,200, it will should get cropped.
But why does Firefox renders it like there is no offset?
It is entirely a bug of how Firefox renders non-repeating CanvasPatterns.
Using a bitmap image will make it obvious:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var img = new Image();
img.onload = function() {
ctx.fillStyle = ctx.createPattern(img, "no-repeat");
ctx.translate(100, 100);
ctx.rect(-100, -100, 200, 200);
ctx.fill();
ctx.stroke();
};
img.src = 'https://mdn.mozillademos.org/files/222/Canvas_createpattern.png';
<canvas id="canvas" width="500" height="250"></canvas>
Results in my Firefox 64:

So when you used a solid red color as pattern, this bug would actually produce a fully red bitmap, while the correct behavior according to specs is to render transparent black pixels where the pattern algorithm created no results.
For a fix, we'll have to wait for FF to provide it.
But note that in your case, using fillRect
will workaround the bug.
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var img = new Image();
img.onload = function() {
ctx.fillStyle = ctx.createPattern(img, "no-repeat");
ctx.fillRect(0, 0, 200, 200);
ctx.strokeRect(0, 0, 200, 200);
};
img.src = 'https://mdn.mozillademos.org/files/222/Canvas_createpattern.png';
<canvas id="canvas" width="500" height="250"></canvas>