【问题标题】:HTML5 Canvas: Applying a gradient to shadowHTML5 Canvas:对阴影应用渐变
【发布时间】:2015-11-26 05:23:44
【问题描述】:

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

这是比较。虽然变化很细微,但右图大致显示了预期的效果。

有没有更好的方法来做到这一点?我想有一种比多次绘制同一件事更有效的方法。有谁知道提供这种功能的库?

【问题讨论】:

    标签: javascript html canvas


    【解决方案1】:

    使用画布 2d 上下文的 filter 属性。 MDN filter 虽然(像往常一样)它确实说 Chrome 不支持过滤器,但它在 Beta 版上已经有一段时间了。对于 IE,我不知道,对于 FF,它已经支持了一段时间。如果您使用它,则必须对其进行测试。

    更新

    支持不是自动的。虽然 MDN 显示对 Firefox 的支持,但您必须将 canvas.filters.enable 设置为 true(无论如何,我相信 Firefox 爱好者都知道)和 chrome 的接缝您必须转到 chrome://flags 然后将 experimental canvas features 设置为 enabled

    更多 我添加了一个后备,因为支持有限。它使用第二个画布通过使用ctx.imageSmoothingEnabled=true; 来模糊阴影,并以模糊量的一半进行渲染。因此,如果模糊为 5 像素,则背景画布中的大小必须为十分之一。然后在原始画布上以全尺寸渲染背景画布并开启平滑处理。

    没有最好的结果,对线也没有好处,但它的速度很快,可以用来优化结果。

    显示如何检测支持和使用的片段。

    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;
    }
    &lt;canvas id="canV" width = 200 height =200&gt;&lt;/canvas&gt;

    【讨论】:

    • 在我拥有的最新浏览器(Chrome 46.0.2490.86、Firefox 42.0 和 IE 11.0.22)上运行您的 sn-p,在 all 中显示“不支持过滤器”其中。不过,这看起来像是对 canvas API 的一个有希望的补充。
    • 抱歉刚刚阅读了 FF 需要激活它。我已经为 sn-p 添加了一个修复程序,如果我做得正确,它应该可以在 Firefox 上运行,因为它从 V35 开始就支持 FF。我使用的是 Chrome 47.0.2526.69 beta-m,但我可能打开了实验性画布功能。我认为他们都害怕它,因为使用过滤器非常慢,肯定不是在动画中使用的东西。
    • 我的错.. 必须是 FF canvas.filters.enabled = true 的浏览器标志。 MDN 清澈如泥。
    • 即使在您添加了canvas.filters.enabled = true 之后,我仍然看到相同的行为。可能需要在浏览器设置中启用那些实验性的画布功能或其他东西。无论如何,我个人需要一些可以在所有现代浏览器上运行的东西。
    • 你如何做是一种方式。另一种方法是在一半、四分之一、1/8 大小的画布上渲染,具体取决于模糊量(越小越模糊),然后使用ctx.imageSmoothingEnabled=true; 以更快的速度将其渲染回全尺寸(一旦你有内存中的新画布以供使用)并且做得合理但并不完美。
    猜你喜欢
    • 2013-06-08
    • 2017-11-18
    • 1970-01-01
    • 1970-01-01
    • 2021-10-05
    • 2011-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多