Canvas 在画布上用渐变绘制一条线

Canvas 在画布上用渐变绘制一条线,canvas,pixi.js,Canvas,Pixi.js,我希望这篇文章不要重复 我想画一条线,如图所示,可能有不同的线宽和梯度。我尝试了createLinearGradient,但它不是我所期望的。我应该用图像来代替吗?或者如何渲染上面的线条 我可以和小精灵一起工作 更新: 我现在可以用渐变色生成线条,但是如何创建动态宽度的线条呢 $(函数(){ var canvas=document.getElementById(“canvas”), ctx=canvas.getContext(“2d”), 绘画=假, lastX=0, lastY=0; ca

我希望这篇文章不要重复

我想画一条线,如图所示,可能有不同的线宽和梯度。我尝试了createLinearGradient,但它不是我所期望的。我应该用图像来代替吗?或者如何渲染上面的线条

我可以和小精灵一起工作

更新: 我现在可以用渐变色生成线条,但是如何创建动态宽度的线条呢

$(函数(){
var canvas=document.getElementById(“canvas”),
ctx=canvas.getContext(“2d”),
绘画=假,
lastX=0,
lastY=0;
canvas.onmousedown=函数(e){
如果(!绘画){
绘画=真实;
}否则{
绘画=假;
}
lastX=e.pageX-this.offsetLeft;
lastY=e.pageY-this.offsetTop;
ctx.lineJoin=ctx.lineCap='round';
};
var img=新图像();
img.src=”http://i.imgur.com/K6qXHJm.png";
canvas.onmousemove=函数(e){
如果(绘画){
mouseX=e.pageX-this.offsetLeft;
mouseY=e.pageY-this.offsetTop;
//var grad=ctx.createLinearGradient(lastX、lastY、mouseX、mouseY);
//渐变加色停止(0,“红色”);
//渐变加色停止(1,“绿色”);
//ctx.strokeStyle=梯度;
ctx.lineWidth=15;
//createPattern(img,“repeat”);
ctx.strokeStyle=ctx.createPattern(img,“repeat”);
ctx.beginPath();
ctx.moveTo(lastX,lastY);
ctx.lineTo(mouseX,mouseY);
ctx.stroke();
$('#output').html('current:'+mouseX+','+mouseY+'
last:'+lastX+','+lastY+'
mousedown:'+“mousedown”); lastX=鼠标; 拉蒂=老鼠; } } 函数衰减(){ ctx.fillStyle=“rgba(255255,0.3)”; ctx.fillRect(0,0,canvas.width,canvas.height); 设置超时(衰减,100); } 淡出(); });

使用2D画布API进行自定义线条渲染 没有简单的方法可以在不牺牲大量质量的情况下创建所需的线条类型

为了获得最佳质量,您需要将线渲染为一组垂直于线并沿线长度的小条带。对于每个部分,计算宽度和颜色,然后渲染该条带

下图将帮助解释我的意思

在中间的直线是定义曲线。外线显示变化的宽度。标记为A的部分为单条(放大)

将直线分成相等的小部分,对于直线上的每个点,需要找到直线上的位置以及与直线上该点垂直的向量。然后以正确的距离找到该点上方和下方的点,使宽度成为该点的直线

然后以正确的颜色绘制每个条带

问题是2D API在连接单独的渲染路径时非常糟糕,因此此方法将由于每个条带之间的抗锯齿而产生垂直线图案

您可以通过使用相同的颜色笔划勾勒每个条带来解决这一问题,但这会破坏外边缘的质量,在线条外边缘的每个接缝处产生小凸起

如果将剪辑区域设置为直线,则可以停止此操作。您可以通过绘制线的轮廓并将其设置为剪辑来完成此操作

然后可以以可接受的质量渲染线

有太多的数学问题无法用一个答案来解释。您需要在贝塞尔曲线上找到点和切线,需要插值渐变,并且需要一种定义平滑宽度函数(另一个贝塞尔)的方法,或者如示例中的复杂抛物线(函数<代码>曲线

例子 下面的示例将从单个贝塞尔曲线(第二阶和第三阶)创建所需的线条类型。可以使用多条曲线和线段对其进行调整

这是您可以获得的最佳质量(尽管您可以渲染2或4倍的分辨率和向下采样以获得轻微的改进)

对于像素完美的抗锯齿结果,您必须使用webGL渲染最终路径(但仍需要像示例中那样生成路径)

const ctx=canvas.getContext(“2d”);
canvas.height=canvas.width=400;
//为bezier曲线使用vecAt和tangentAsVec所需的最小groover.geom库。
常数geom=(()=>{
常数v1=新向量();
常数v2=新向量();
const v3=新的向量();
常数v4=新向量();
函数Vec(x,y){
这个.x=x;
这个。y=y;
};
函数Bezier(p1,p2,cp1,cp2){
这是1.p1=p1;
这是p2=p2;
这是cp1=cp1;
这是cp2=cp2;
}    
Bezier.prototype={
//======================================================================================
//二阶(a,b,c)和三阶(a,b,c,d)贝塞尔的一维多项式
//======================================================================================
//对于二次f(t)=a(1-t)^2+2b(1-t)t+ct^2
//=a+2(-a+b)t+(a-2b+c)t^2
//导数f’(t)=2(1-t)(b-a)+2(c-b)t
//======================================================================================
//对于立方f(t)=a(1-t)^3+3bt(1-t)^2+3c(1-t)t^2+dt^3
//=a+(-2a+3b)t+(2a-6b+3c)t^2+(-a+3b-3c+d)t^3
//导数f’(t)=-3a(1-t)^2+b(3(1-t)^2-6(1-t)t)+c(6(1-t)t-3t^2)+3dt^2
//二阶导数f“(t)=6(1-t)(c-2b+a)+6t(d-2c+b)
//======================================================================================        
p1:未定义,
p2:未定义,
cp1:未定义,
cp2:未定义,
vecAt(位置,vec){
var c;
如果(vec==未定义)