仅变换Path2D的样式 [英] Transform only the styling of a Path2D

查看:62
本文介绍了仅变换Path2D的样式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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.

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

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>

使用 Path2D API,我们必须通过直接在 ctx.fill(path) ctx.stroke(path)方法中使用此子路径.
这意味着我们无法像以前一样将样式与子路径声明分开:

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>

使用此方便的Path2D API时是否无法执行此操作?

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.addPath method.

因此,我们实际上可以通过传递Path2d的转换后的副本来达到相同的结果:

So we can actually achieve the same result by passing a transformed copy of our 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 参数,但这是我所知道的唯一方法...

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

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)中使用此类对象.

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

这篇关于仅变换Path2D的样式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆