In the canvas 2D API, we can first define a subpath using one context's transformation and then change that context's transformation for only the fill() or stroke() calls, which would have effect on the stylings, like fillStyle, lineWidth and other visible properties, but which will leave the sub-path as defined. This is quite convenient when we want to zoom in vector-shapes while keeping the same stroke-width.
Here is a simple example where only the lineWidth is affected by the variable zoom transformation:
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
let zoom = 1;
let speed = 0.1;
requestAnimationFrame(update);
function update() {
if( zoom >= 10 || zoom <= 0.1 ) speed *= -1;
zoom += speed;
draw();
requestAnimationFrame(update);
}
function draw() {
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0,0,canvas.width,canvas.height);
// define the subpath at identity matrix
ctx.beginPath();
ctx.moveTo(10 ,80);
ctx.quadraticCurveTo(52.5,10,95,80);
ctx.quadraticCurveTo(137.5,150,180,80);
// stroke zoomed
ctx.setTransform(zoom, 0, 0, zoom, 0, 0);
ctx.stroke();
}
<canvas id="canvas"></canvas>
With the Path2D API, we have to pass this subpath directly in either ctx.fill(path) or ctx.stroke(path) methods.
This means we can't separate the stylings from the subpath declaration like we did before:
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
let zoom = 1;
let speed = 0.1;
requestAnimationFrame(update);
function update() {
if( zoom >= 10 || zoom <= 0.1 ) speed *= -1;
zoom += speed;
draw();
requestAnimationFrame(update);
}
function draw() {
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0,0,canvas.width,canvas.height);
// define the subpath at identity matrix
// (declared in 'draw' just for the example, would be the same anyway outside)
const path = new Path2D("M 10 80 Q 52.5 10, 95 80 T 180 80");
// stroke zoomed
ctx.setTransform(zoom, 0, 0, zoom, 0, 0);
ctx.stroke(path);
}
<canvas id="canvas"></canvas>
Is there no way of doing this while using this otherwise convenient Path2D API?
There is a way to transform a Path2D object by passing a DOMMatrix1 to the
Path2D.prototype.addPathmethod.So we can actually achieve the same result by passing a transformed copy of our Path2d:
However, you'll notice that we have to make our path-matrix relatively from the styling one.
The new DOMMatrix API eases matrix transforms a lot2, but it makes this approach definitely more convoluted than the
beginPath()way, that's quite unfortunate we can't act on the Path2D object itself or even just have this transform parameter on the constructor too, but that's the only way I know of...1. Actually it doesn't need to be an actual DOMMatrix, any object with its properties will do
2. We can now even use such objects in
ctx.setTransform(matrix).