繁体   English   中英

仅变换 Path2D 的样式

[英]Transform only the styling of a Path2D

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

这是一个简单的示例,其中只有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)ctx.stroke(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 时,有没有办法做到这一点?

有一种方法通过使以变换Path2D对象DOMMatrix 1Path2D.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 ,但它使这种方法肯定比beginPath()方法更复杂,很遗憾我们不能对 Path2D 对象本身采取行动,甚至也不能在构造函数上使用这个变换参数,但这是我所知道的唯一方法......

 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)使用这样的对象。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM