Javascript canvas 填充具有公共边框的形状后的间隙

Javascript canvas gap after fill of shapes with common border

我想了解并了解我在用共享边框填充两个 shapes/paths 时遇到的问题的解决方案,即在为所有形状调用填充后仍然 它们之间存在微小的间隙

该片段展示了绘制相关内容的代码 shapes/paths:

ctx.beginPath();
ctx.moveTo(pBottom.x, pBottom.y);
ctx.lineTo(0, 0);
ctx.lineTo(pTop.x, pTop.y);
ctx.lineTo(pTopFront.x, pTopFront.y);
ctx.lineTo(pBottomFront.x, pBottomFront.y);
ctx.fillStyle = gradientTopSide;
ctx.fill();
ctx.fillStyle = gradientBottomSide;
ctx.fill();

ctx.beginPath();
ctx.moveTo(pBottom.x, pBottom.y);
ctx.arc(0, 0, radiusBackArc, (angleBottomBack) * Math.PI / 180, (angleTopBack) * Math.PI / 180);
ctx.lineTo(0, 0);
ctx.fillStyle = gradientBackArc;
ctx.fill();

左侧(蓝色圆圈处)、顶部和底部都可以看到间隙。它们是径向和线性渐变填充相交的地方。

我想玩一下 canvas 来创建一个简单的 light/torch 效果,这些台词毁了我的乐趣。当然是因为我不知道如何在不破坏渐变效果的情况下以一种好的方式填充它们。

Please find the JSFiddle presenting the issue.

当你处理涉及透明度的渐变时,你会 运行 进入重叠,其中 alpha 通道值将乘以子像素化,点的舍入误差以及可能的梯度计算。

很可能您在代码中进行的计算必须正确舍入。如果不是,您将需要 canvas 对这些像素进行子像素化,并且很难保持简洁的结果,尤其是在涉及 alpha 时。这是canvas的怪癖和这种绘制渐变线的方式。 Canvas 也没有正确地 "bend" (连接)在角落里,所以你得到重叠的图纸 - 我们对那部分无能为力。

我看到有两种方法可以解决这个很简单的方法:

  • 使用您在其中绘制的图像,而不是构建形状 - 这样速度很快,结果也一样好,除非您需要不同的尺寸(如果是动画,则不需要)虽然很重要)。我个人会选择这个选项(你也会获得更多的艺术自由)。
  • 用不同的技巧绘制形状:

我将在此处演示后者,作为奖励,您可以删除大量代码。如果你需要透明度而不是你应该选择选项 1。而且,它也不是完美的,因为我们仍然必须依赖 canvas' 的方式来做到这一点,但它可能是一个改进的替代品重叠问题:

  • 使用形状的单一闭合路径
  • 通过删除最后一个 lineTo() 修改当前形状并将其替换为 closePath()
  • 重要:将 lineJoin 更改为 round
  • 我们创建一个循环,在其中绘制重叠的形状,并且对于每次迭代:
    • 根据迭代更改纯色
    • 略微扩展
    • 略微移动

我在这里做了一个简单的版本,但如果您选择使用它,我会留给您找出更令人满意的值细节。

var d2r = Math.PI / 180, i = 0, iterations = 14;
ctx.lineJoin = "round";
ctx.lineWidth = 3;

for(; i < iterations; i++) {

    ctx.beginPath();
    ctx.moveTo(pBottom.x, pBottom.y);
    ctx.arc(0, 0, radiusBackArc, angleBottomBack * d2r, angleTopBack * d2r);
    ctx.lineTo(pTopFront.x, pTopFront.y);
    ctx.arc(0, 0, radiusFrontArc, angleTopFront * d2r, angleBottomFront * d2r);
    // don't set endpoint=startpoint, instead just close the shape
    ctx.closePath();

    // using HSL will allow us to easily set a gradient ligthness:
    ctx.strokeStyle = "hsl(0, 0%, " +  (i/iterations*100) + "%)";
    ctx.stroke();
    ctx.translate(i*0.1,-i*0.02);
    ctx.scale(0.99, 0.98);
}

Modified fiddle here