我很惊讶地发现,显然画布API不允许您将渐变应用于阴影,如下所示:
var grad = ctx.createLinearGradient(fromX, fromY, toX, toY);
grad.addColorStop(0, "red");
grad.addColorStop(1, "blue");
ctx.strokeStyle = grad;
ctx.lineWidth = 3;
ctx.shadowBlur = 10;
ctx.shadowColor = grad; // doesn't seem to work
ctx.beginPath();
ctx.moveTo(fromX, fromY);
ctx.lineTo(toX, toY);
ctx.closePath();
ctx.stroke();
// linear gradient from start to end of line
var canvas = document.getElementById('mycanvas'),
ctx = canvas.getContext('2d'),
fromX = 3,
fromY = 3,
toX = 197,
toY = 197,
grad = ctx.createLinearGradient(fromX, fromY, toX, toY);
canvas.width = 200;
canvas.height = 200;
grad.addColorStop(0, "red");
grad.addColorStop(1, "blue");
ctx.strokeStyle = grad;
ctx.lineWidth = 3;
ctx.shadowBlur = 20;
ctx.shadowColor = grad;
ctx.beginPath();
ctx.moveTo(fromX, fromY);
ctx.lineTo(toX, toY);
ctx.closePath();
ctx.stroke();
body {
background: black
}
<canvas id="mycanvas"></canvas>
一种解决方法是简单地绘制线条/形状/等。多次以不同的大小和不透明度获得类似的结果:
var grad = ctx.createLinearGradient(fromX, fromY, toX, toY);
canvas.width = 200;
canvas.height = 200;
grad.addColorStop(0, "red");
grad.addColorStop(1, "blue");
ctx.strokeStyle = grad;
ctx.lineWidth = 3;
//ctx.shadowBlur = 20;
//ctx.shadowColor = grad;
for (var i = 10; i > 1; i--) {
ctx.lineWidth = i;
ctx.globalAlpha = 1 / i;
ctx.beginPath();
ctx.moveTo(fromX, fromY);
ctx.lineTo(toX, toY);
ctx.closePath();
ctx.stroke();
}
// linear gradient from start to end of line
var canvas = document.getElementById('mycanvas'),
ctx = canvas.getContext('2d'),
fromX = 3,
fromY = 3,
toX = 197,
toY = 197,
grad = ctx.createLinearGradient(fromX, fromY, toX, toY);
canvas.width = 200;
canvas.height = 200;
grad.addColorStop(0, "red");
grad.addColorStop(1, "blue");
ctx.strokeStyle = grad;
ctx.lineWidth = 3;
//ctx.shadowBlur = 20;
//ctx.shadowColor = grad;
for (var i = 10; i > 1; i--) {
ctx.lineWidth = i;
ctx.globalAlpha = 1 / i;
ctx.beginPath();
ctx.moveTo(fromX, fromY);
ctx.lineTo(toX, toY);
ctx.closePath();
ctx.stroke();
}
body {
background: black;
}
<canvas id="mycanvas"></canvas>
这是比较。尽管变化很微妙,但右图显示了大致所需的效果。
有更好的方法吗?我想有一种比多次绘制相同的东西更有效的方法。有谁知道提供这种功能的库?
答案 0 :(得分:2)
使用canvas 2d上下文的filter
属性。 DEMO虽然(像往常一样)确实说Chrome上不支持过滤器,但在Beta版本上已经过了一段时间。对于IE我不知道,对于FF它已经支持了一段时间。如果你使用它,你将不得不测试它。
<强>更新强>
支持不接缝是自动的。虽然MDN显示对Firefox的支持,但你必须将canvas.filters.enable
设置为true
(无论这意味着什么,我相信firefox爱好者都知道)和chrome的接缝你必须转到chrome://flags
然后设置{{ 1}}到experimental canvas features
更多强>
我添加了一个后备,因为有这么有限的支持。它使用第二个画布通过使用enabled
来模糊阴影,并以模糊量的一半进行渲染。因此,如果模糊为5像素,则背景画布必须为十分之一大小。然后在原始画布上以完整尺寸渲染背景画布并平滑。
没有最好的结果,也不会对线条有好处,但它速度快,可以用来优化结果。
显示如何检测支持和使用的代码段。
ctx.imageSmoothingEnabled=true;
var canvas = document.getElementById("canV");
var ctx = canvas.getContext("2d");
var g = ctx.createLinearGradient(10,10,100,100);
for(var i = 0; i <= 1; i+= 0.05){
g.addColorStop(i,"hsl("+Math.floor(i*360)+",100%,50%)");
}
var gDark = ctx.createLinearGradient(20,20,100,100);
for(var i = 0; i <= 1; i+= 0.05){
gDark.addColorStop(i,"hsl("+Math.floor(i*360)+",100%,30%)");
}
ctx.font = "16px Arial";
ctx.textAlign = "center";
ctx.textBaseline = "hanging";
if(ctx.filter !== undefined){
ctx.fillText("Using filter.",65,125);
ctx.fillStyle = gDark;
ctx.filter = "blur(5px)"; // set the blur
ctx.fillRect(20,20,100,100); // draw the shadow
ctx.fillStyle = g; // set the lighter gradoent
ctx.filter = "blur(0px)"; // remove the blur
ctx.lineWidth = 2;
ctx.strokeStyle = "black"
ctx.fillRect(10,10,100,100); // draw the box
ctx.strokeRect(10,10,100,100); // with line to look nice.
}else{
// fallback method
ctx.fillText("Using Fallback.",60,125);
var can = document.createElement("canvas"); // create a second canvas
can.width = Math.floor(canvas.width/10); // size to make one pixel the
can.height =Math.floor(canvas.height/10); // size of the blur
var ctxS = can.getContext("2d");
ctxS.setTransform(1/10,0,0,1/10,0,0); // set scale so can work in same coords
ctxS.fillStyle = gDark;
ctxS.fillRect(20,20,100,100); // draw the shadow
ctx.imageSmoothingEnabled=true;
ctx.drawImage(can,0,0,canvas.width,canvas.height);
}
ctx.fillStyle = g; // set the lighter gradoent
ctx.lineWidth = 2;
ctx.strokeStyle = "black"
ctx.fillRect(10,10,100,100); // draw the box
ctx.strokeRect(10,10,100,100); // with line to look nice.
#canV {
width:200px;
height:200px;
}