仅转换 Path2D 的样式
Transform only the styling of a Path2D
在 canvas 2D API 中,我们可以首先使用一个上下文的转换定义子路径,然后仅针对 fill()
或 stroke()
调用更改该上下文的转换,这会对 样式 产生影响,如 fillStyle
、lineWidth
和其他可见属性,但会保留定义的子路径。当我们想在保持相同笔画宽度的同时放大矢量形状时,这非常方便。
这是一个简单的例子,其中只有 lineWidth
受到变量 zoom 转换的影响:
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>
使用 Path2D API,我们必须在 ctx.fill(path)
或 ctx.stroke(path)
方法中直接传递此子路径。
这意味着我们不能像以前那样将样式与子路径声明分开:
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>
有没有办法在使用这个方便的 Path2D 时做到这一点 API?
有一种方法可以通过传递 DOMMatrix1 to the Path2D.prototype.addPath
方法来转换 Path2D 对象。
所以我们实际上可以通过传递 Path2d 的转换副本来实现相同的结果:
const transformPath = (path, matrix) => {
const copy = new Path2D();
copy.addPath(path, matrix);
return copy;
};
// ...
ctx.stroke( transformPath( path, {a: 1/zoom, d: 1/zoom } );
但是,您会注意到我们必须使 路径矩阵 相对于 样式 一个。
新的 DOMMatrix API 大大简化了矩阵变换 2,但它使这种方法肯定比 beginPath()
方法更复杂,很遗憾我们不能采取行动在 Path2D 对象本身上,甚至在构造函数上也只有这个 transform 参数,但这是我所知道的唯一方法...
const transformPath = (path, matrix) => {
const copy = new Path2D();
copy.addPath(path, matrix);
return copy;
};
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// define the subpath
const path = new Path2D("M 10 80 Q 52.5 10, 95 80 T 180 80");
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);
// zoom the stylings
ctx.setTransform(zoom, 0, 0, zoom, 0, 0);
// create our transformed path
const invertMatrix = {a: 1/zoom, d: 1/zoom};
ctx.stroke(transformPath(path, invertMatrix));
}
<canvas id="canvas"></canvas>
1.实际上它不需要是一个实际的 DOMMatrix,任何具有其属性的对象都可以
2。我们现在甚至可以在 ctx.setTransform(matrix)
.
中使用此类对象
在 canvas 2D API 中,我们可以首先使用一个上下文的转换定义子路径,然后仅针对 fill()
或 stroke()
调用更改该上下文的转换,这会对 样式 产生影响,如 fillStyle
、lineWidth
和其他可见属性,但会保留定义的子路径。当我们想在保持相同笔画宽度的同时放大矢量形状时,这非常方便。
这是一个简单的例子,其中只有 lineWidth
受到变量 zoom 转换的影响:
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>
使用 Path2D API,我们必须在 ctx.fill(path)
或 ctx.stroke(path)
方法中直接传递此子路径。
这意味着我们不能像以前那样将样式与子路径声明分开:
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>
有没有办法在使用这个方便的 Path2D 时做到这一点 API?
有一种方法可以通过传递 DOMMatrix1 to the Path2D.prototype.addPath
方法来转换 Path2D 对象。
所以我们实际上可以通过传递 Path2d 的转换副本来实现相同的结果:
const transformPath = (path, matrix) => {
const copy = new Path2D();
copy.addPath(path, matrix);
return copy;
};
// ...
ctx.stroke( transformPath( path, {a: 1/zoom, d: 1/zoom } );
但是,您会注意到我们必须使 路径矩阵 相对于 样式 一个。
新的 DOMMatrix API 大大简化了矩阵变换 2,但它使这种方法肯定比 beginPath()
方法更复杂,很遗憾我们不能采取行动在 Path2D 对象本身上,甚至在构造函数上也只有这个 transform 参数,但这是我所知道的唯一方法...
const transformPath = (path, matrix) => {
const copy = new Path2D();
copy.addPath(path, matrix);
return copy;
};
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// define the subpath
const path = new Path2D("M 10 80 Q 52.5 10, 95 80 T 180 80");
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);
// zoom the stylings
ctx.setTransform(zoom, 0, 0, zoom, 0, 0);
// create our transformed path
const invertMatrix = {a: 1/zoom, d: 1/zoom};
ctx.stroke(transformPath(path, invertMatrix));
}
<canvas id="canvas"></canvas>
1.实际上它不需要是一个实际的 DOMMatrix,任何具有其属性的对象都可以
2。我们现在甚至可以在 ctx.setTransform(matrix)
.