如何使用 HTML5 画布创建软笔触边缘

Posted

技术标签:

【中文标题】如何使用 HTML5 画布创建软笔触边缘【英文标题】:how to create soft stroke edge using HTML5 canvas 【发布时间】:2012-02-19 02:47:46 【问题描述】:

我正在使用 html5 画布创建一个绘图应用程序。

https://github.com/homanchou/sketchyPad

我可以使用 rgba 来控制我的线条笔触的不透明度,但是如何实现软羽毛笔刷边缘与硬圆形边缘?

【问题讨论】:

【参考方案1】:

三种可能的解决方案:

    您可以将线条写入屏幕外画布,应用模糊滤镜,然后将结果绘制到可见画布中。

    如果您只使用直线段,您可以为每个线段使用线性渐变。渐变的方向必须与线段的方向成 90" 角。

    在同一位置多次绘制相同的线。首先是全宽和低 alpha。然后减小宽度并增加 alpha。

对每个线段使用线性渐变的示例:

http://jsfiddle.net/chdh/MmYAt/

function drawSoftLine(x1, y1, x2, y2, lineWidth, r, g, b, a) 
   var lx = x2 - x1;
   var ly = y2 - y1;
   var lineLength = Math.sqrt(lx*lx + ly*ly);
   var wy = lx / lineLength * lineWidth;
   var wx = ly / lineLength * lineWidth;
   var gradient = ctx.createLinearGradient(x1-wx/2, y1+wy/2, x1+wx/2, y1-wy/2);
      // The gradient must be defined accross the line, 90° turned compared
      // to the line direction.
   gradient.addColorStop(0,    "rgba("+r+","+g+","+b+",0)");
   gradient.addColorStop(0.43, "rgba("+r+","+g+","+b+","+a+")");
   gradient.addColorStop(0.57, "rgba("+r+","+g+","+b+","+a+")");
   gradient.addColorStop(1,    "rgba("+r+","+g+","+b+",0)");
   ctx.save();
   ctx.beginPath();
   ctx.lineWidth = lineWidth;
   ctx.strokeStyle = gradient;
   ctx.moveTo(x1, y1);
   ctx.lineTo(x2, y2);
   ctx.stroke();
   ctx.restore(); 

通过减小宽度和增加 alpha 来多次绘制线条的示例:

http://jsfiddle.net/chdh/RmtxL/

function drawSoftLine(x1, y1, x2, y2, lineWidth, r, g, b, a) 
   ctx.save();
   var widths = [1   , 0.8 , 0.6 , 0.4 , 0.2  ];
   var alphas = [0.2 , 0.4 , 0.6 , 0.8 , 1    ];
   var previousAlpha = 0;
   for (var pass = 0; pass < widths.length; pass++) 
      ctx.beginPath();
      ctx.lineWidth = lineWidth * widths[pass];
      var alpha = a * alphas[pass];
      // Formula: (1 - alpha) = (1 - deltaAlpha) * (1 - previousAlpha)
      var deltaAlpha = 1 - (1 - alpha) / (1 - previousAlpha)
      ctx.strokeStyle = "rgba(" + r + "," + g + "," + b + "," + deltaAlpha + ")";
      ctx.moveTo(x1, y1);
      ctx.lineTo(x2, y2);
      ctx.stroke();
      previousAlpha = alpha; 
   ctx.restore(); 

【讨论】:

我创建了一个 jsperf 测试来比较这两种选择的性能:jsperf.com/feathered-canvas-line。看起来画多条线稍微快一点,但我觉得它看起来丑很多。【参考方案2】:

我很确定这取决于您使用的浏览器。上次我检查(不久前 - 可能已经改变)Firefox 和 Chrome 没有抗锯齿边缘,而 IE9 有。

【讨论】:

我不是指抗锯齿边缘,它只会影响像素的薄外层。看看muro.deviantart.com,他们是如何实现几个像素层深的非常柔软的边缘? 这个答案在 2020 年仍然正确吗?感谢您的帮助!【参考方案3】:

您可以使用 css 过滤器来模糊画布。 SVG rasterization trick 是可能的。以下是你的做法:

    制作两幅画布,一个在另一个之上。其中一个叫«Target»,另一个叫«Buffer»。缓冲区是您绘制的对象,目标是生成的画布。

    css-filter: blur(px) 应用到缓冲区画布,以便用户可以立即看到模糊的预览。

    这是有趣的部分。在每个笔划上(即在 mouseup 上),光栅化 Buffer 画布,将图像放入 &lt;svg&gt;&lt;foreignObject&gt;&lt;/foreignObject&gt;&lt;/svg&gt;,对其应用相同的 CSS 过滤器,光栅化 SVG,然后将光栅化的 SVG 放在目标画布上。这是gist with code example。

【讨论】:

这还是2020年最好的答案吗?感谢您的帮助!【参考方案4】:

将线条提交到画布后,您可以使用 CanvasPixelArray 类进行柔化(模糊)。只需将颜色添加到相邻像素即可。 Here is a good info on pixel manipulation .

【讨论】:

以上是关于如何使用 HTML5 画布创建软笔触边缘的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 jquery 将动态创建的 img 元素加载到 HTML5 画布中

调整 HTML5 画布元素的大小

如何使用 html5 画布扭曲图像以创建在风中飘扬的旗帜效果

如何创建中间有孔的画布 html5 剪辑区域?

如何将画布保存为html5中的图像?

如何在 Flash html5 画布中更改影片剪辑的宽度?