1

在 canvas 2D API 中,我们可以首先使用一个上下文的转换定义一个子路径,然后只为fill()orstroke()调用更改该上下文的转换,这将对样式fillStyle、likelineWidth和其他可见属性产生影响,但会留下子路径。定义的路径。当我们想要放大矢量形状同时保持相同的笔划宽度时,这非常方便。

这是一个简单的示例,其中只有lineWidth受可变缩放转换影响:

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)orctx.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 时有没有办法做到这一点?

4

1 回答 1

2

有一种方法可以通过将DOMMatrix 1传递给方法来转换 Path2D 对象Path2D.prototype.addPath

因此,我们实际上可以通过传递 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,但它使这种方法肯定比方法更复杂,这很不幸,我们不能对 Path2D 对象本身进行操作,甚至也不能在构造函数上使用这个转换参数,但那是我知道的唯一方法...
beginPath()

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).

于 2020-01-10T07:55:44.657 回答