HTML5 Canvas:将渐变应用于阴影

时间:2015-11-25 19:26:37

标签: javascript html html5 canvas

我很惊讶地发现,显然画布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>

这是比较。尽管变化很微妙,但右图显示了大致所需的效果。

line1 line2

有更好的方法吗?我想有一种比多次绘制相同的东西更有效的方法。有谁知道提供这种功能的库?

1 个答案:

答案 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;
}