在HTML5画布中,我可以用径向渐变描边形状,参见https://jsfiddle.net/s03gz7wy/1/
var cnv = document.createElement("canvas"),
ctx = cnv.getContext("2d");
document.body.appendChild(cnv);
ctx.lineWidth = 10;
ctx.rect(20, 20, 100, 100);
var g = ctx.strokeStyle = ctx.createRadialGradient(70, 70, 0, 70, 70, 100);
g.addColorStop(0.4, "blue");
g.addColorStop(0.7, "red");
//ctx.translate(70,70); ctx.scale(0.5,1); ctx.translate(-70,-70);
ctx.stroke();
我希望渐变为"挤压";像椭圆一样,所以我对画布应用变换(第10行-尝试取消注释)。然而,它也改变了笔画(在某些地方更细)。
有没有办法用椭圆渐变描边呢?在2D环境中?这在SVG和PDF中是可能的,但我不能在画布上这样做。
这确实是Canvas 2D API缺少的功能,好消息是,它正在积极讨论CanvasPattern
对象拥有的相同的setTransform()
方法将被添加到CanvasGradient
接口(以及其他缺失的梯度功能,如定义扩展方法,或用于插值的色彩空间,也许还有其他)。考虑到关于这个主题的最新讨论,我预计它将成为2023年API的新增内容之一。
但是目前,恐怕我们只能用合成了。
最灵活的解决方案之一是使用另一个画布进行合成,然后将该画布绘制在可见画布上。步骤可以是:
- 在透明画布上绘制描边(纯色)。
- 将上下文的
globalCompositeOperation
模式更改为"source-in"
。 - 设置你的变换
- 使用
CanvasGradient
填充规则填充一个与画布一样大的矩形。
(您可以通过更改gCO模式来重新排序此列表)。
const cnv = document.createElement("canvas");
const ctx = cnv.getContext("2d");
document.body.appendChild(cnv);
// create a new detached canvas the same size as the visible one,
// this allows to not affect the previous drawings on the visible canvas
const off = cnv.cloneNode();
const oCtx = off.getContext("2d");
oCtx.lineWidth = 10;
oCtx.rect(20, 20, 100, 100);
oCtx.stroke();
// next drawing will "stay" only where already painted
oCtx.globalCompositeOperation = "source-in";
// note: we set the 'fillStyle' to 'g', not the 'strokeStyle'
var g = oCtx.fillStyle = oCtx.createRadialGradient(70, 70, 0, 70, 70, 100);
g.addColorStop(0.4, "blue");
g.addColorStop(0.7, "red");
oCtx.translate(70, 70);
oCtx.scale(0.5, 1);
oCtx.translate(-70, -70);
// in order for our fillRect call to cover the whole canvas
// even when the context is transformed, we need to transform
// our points through the inverse CTM
const invertMat = oCtx.getTransform().invertSelf();
const transformedXY = invertMat.transformPoint({ x: 0, y: 0 });
const transformedWH = invertMat.transformPoint({ x: off.width, y: off.height });
oCtx.fillRect(transformedXY.x, transformedXY.y, transformedWH.x, transformedWH.y);
// Draw back to the visible canvas
ctx.drawImage(off, 0, 0);