相交半透明描边文字

Intersected semi-transparent stroke text

我对绘画 context.stokeText 有疑问,该样式包含 alpha。较大的线宽值会产生一些相交笔画的效果,因此颜色会更深。 我该如何避免这种情况?

ctx.strokeStyle ="rgba(0,0,0,0.3)";                
ctx.lineWidth = 15;
ctx.lineJoin="round";                
ctx.strokeText(text, x, y);

Image

这在规范中有点不一致,因为通常 overlapping sub-pathes are painted only once

但是 strokeText() create one shape per glyph,因此这种方法确实会自己绘制每个字形,从而创建可见的重叠。

要克服这个问题,您需要有点创意:

  • 首先把你的文字画成完全不透明的,
  • 然后使用所需的 alpha 级别重绘生成的像素(有多种方法)。
  • 在您的场景中绘制(或在后面绘制背景)。

这里有几种方法(还有很多其他方法):

可能是最简单的,但会占用更多内存:使用第二个断开连接的 canvas:

const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");

// create a new canvas just for the text
const canvas2 = canvas.cloneNode();
const ctx2 = canvas2.getContext("2d");
ctx2.font = "60px sans-serif";
const text = "MY TEXT";
const x = canvas.width - ctx2.measureText(text).width - 20;
const y = canvas.height - 20;
// draw it fully opaque
ctx2.lineWidth = 15;
ctx2.lineJoin="round";                
ctx2.strokeText(text, x, y);

// draw the background on the visible canvas
ctx.fillStyle = "#ffe97f";
ctx.fillRect(0, 0, canvas.width, canvas.height);

// now draw our text canvas onto the visible one
// with the desired opacity
ctx.globalAlpha = 0.3;
ctx.drawImage(canvas2, 0, 0);
<canvas width="465" height="234"></canvas>

内存更友好,但这需要您在不同的方向重写绘图逻辑,使用 compositing:

const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
ctx.font = "60px sans-serif";
const text = "MY TEXT";
const x = canvas.width - ctx.measureText(text).width - 20;
const y = canvas.height - 20;

// first draw the text fully opaque
ctx.lineWidth = 15;
ctx.lineJoin="round";                
ctx.strokeText(text, x, y);

// now apply the opacity
ctx.fillStyle ="rgba(0,0,0,0.3)";
ctx.globalCompositeOperation = "source-in";
ctx.fillRect(0, 0, canvas.width, canvas.height);

// and the background
ctx.fillStyle = "#ffe97f";
ctx.globalCompositeOperation = "destination-over";
ctx.fillRect(0, 0, canvas.width, canvas.height);

// if you want to keep drawing "normaly"
ctx.globalCompositeOperation = "source-over";
<canvas width="465" height="234"></canvas>

两者的混合,具有不同的合成规则:

const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
ctx.font = "60px sans-serif";
const text = "MY TEXT";
const x = canvas.width - ctx.measureText(text).width - 20;
const y = canvas.height - 20;

// first draw the text fully opaque
ctx.lineWidth = 15;
ctx.lineJoin="round";                
ctx.strokeText(text, x, y);

// now redraw over itself with the desired opacity
ctx.globalAlpha = 0.3;
ctx.globalCompositeOperation = "copy";
ctx.drawImage(canvas, 0, 0);
ctx.globalAlpha = 1;

// and the background
ctx.fillStyle = "#ffe97f";
ctx.globalCompositeOperation = "destination-over";
ctx.fillRect(0, 0, canvas.width, canvas.height);

// if you want to keep drawing "normaly"
ctx.globalCompositeOperation = "source-over";
<canvas width="465" height="234"></canvas>