缩放画布而不会扭曲lineWidth或破坏剪辑

时间:2018-08-09 15:31:27

标签: html5-canvas

我想缩放工程图中的一个轴。正如其他人指出的那样,天真地这样做也会在描边路径时缩放lineWidth。其他解决方案也指出,您可以执行以下操作以避免扭曲lineWidth:

ctx.save();
ctx.scale(1, 2);
ctx.beginPath();
// draw shape
ctx.restore();
ctx.stroke();

不幸的是,我还需要一个复杂的剪切区域,并且restore()会破坏剪切区域。我尝试简单地恢复规模:

ctx.save();
ctx.scale(1, 2);
ctx.beginPath();
// draw shape
ctx.scale(1, 1);
ctx.stroke();

但是那没有用,并且lineWidth仍然失真。

这是对物理系统的建模,涉及的形状既是圆弧又是线。重要的是,各种形状的交点必须准确,因此在没有剪辑的情况下进行计算和计算所有可能的交点会变得更加困难。同样,如果没有缩放比例,则将强制执行椭圆曲线,这是我要避免的做法,但这是最后的选择。

有什么想法吗?

1 个答案:

答案 0 :(得分:1)

我不清楚您到底在做什么,我想您可以通过使用compositing来避免剪切,这可以提供更好的性能和更清洁的结果。

但是无论如何,让我们假设您绝对要剪辑...


ctx.save()将已保存的状态堆叠在内部ArrayLike对象中。每次对restore()的调用只会恢复pop取出的元素(即堆栈中最新的元素)。

因此,只需在这两个操作之间调用ctx.save(),就可以很好地恢复缩放前和裁剪后的状态。

ctx = c.getContext('2d');
ctx.save(); // stack a first state without clipping, just in case
// define our clipping region
ctx.arc(53,50,30,0,Math.PI*2);
ctx.stroke();
ctx.clip();

// draw once at normal scale
drawShape();
ctx.strokeStyle = 'blue';
ctx.stroke();

// save our context state before we scale
ctx.save();
// now draw scaled
ctx.scale(2, 1);
drawShape();
// restore to before we applied the scale
ctx.restore();
ctx.strokeStyle = 'red';
ctx.stroke();

// restore before clipping
ctx.restore();
// next drawings would be unclipped...

function drawShape() {
  ctx.beginPath();
  ctx.rect(25, 30, 20, 20);
}
<canvas id="c"></cavnas>



现在,您甚至不必管理这种混乱的上下文状态,只需恢复上下文的转换矩阵即可。

在您的代码中,您使用ctx.scale(1, 1)。这是无人操作(不会做任何事情)。 ctx.scale(factor, factor)将当前比例值乘以您传递的factor。因此,既然 n * 1 = n ,那将无济于事。

所以是的,您可以计算需要传递到那里的逆因子(例如在您的示例中,它应该是ctx.scale(1, 0.5)),但是最简单的解决方案是使用绝对值setTransform(xScale, xSkew, ySkew, yScale, xTranslate, yTranslate)方法。
要将上下文矩阵重置为其默认值,只需记住ctx.setTransform(1,0,0,1,0,0);

ctx = c.getContext('2d');
// as a bonus, we will use compositing instead of clipping
// but this has no incidence on the demo

// draw once at normal scale
drawShape();
ctx.strokeStyle = 'blue';
ctx.stroke();

// now draw scaled
ctx.scale(2, 1);
drawShape();
// restore the context's matrix
ctx.setTransform(1,0,0,1,0,0);
ctx.strokeStyle = 'red';
ctx.stroke();

// clipping (compositing)
// only the pixels that are currenlty on the context,
// and whose position will match with one of the to be drawn will be kept
ctx.globalCompositeOperation = 'destination-in';
ctx.beginPath();
ctx.arc(53,50,30,0,Math.PI*2);
ctx.fill();
// restore to default
ctx.globalCompositeOperation = 'source-over';
// just to show the clipped area
ctx.strokeStyle = '#000';
ctx.stroke();

function drawShape() {
  ctx.beginPath();
  ctx.rect(25, 30, 20, 20);
}
<canvas id="c"></canvas>