在HTML画布上绘制具有不同不透明度级别的笔划

时间:2014-01-04 20:05:50

标签: html canvas blending compositing

问题

我正在尝试使用不透明度抖动创建一个画笔工具(就像在Photoshop中一样)。具体问题是:

在具有不同不透明度级别的HTML画布上绘制笔划。具有较高不透明度的像素应替换具有较低不透明度的像素;否则,像素保持不变。

在此过程中不应丢失透明度。笔划在单独的画布上绘制,然后与背景画布合并。

结果应该类似于 this 。所有代码和相应的输出都可以在 here (JSFiddle)找到。

因为您不能使用不同级别的不透明度描绘单个路径(如果我错了,请纠正我)我的代码会为每个具有不同不透明度的段创建路径。

非解决方案1,使用'变暗'混合模式

当使用不透明像素时,变暗混合模式会产生所需的结果,但似乎不能使用透明度。放松透明度是一个破坏者。

使用不透明像素:

enter image description here

使用透明像素:

enter image description here

非解决方案2,使用'destination-out'合成运算符

在绘制新笔划段之前,使用“destination-out”合成运算符从相邻像素中减去其不透明度。然后使用'source-over'添加新的笔划段。这种方法几乎可以工作,但它有点偏离。

enter image description here

寻找解决方案

我想避免手工操作每个像素(我以前做过)。我错过了一些明显的东西吗这个问题有一个简单的解决方案吗?

"Links to jsfiddle.net must be accompanied by code."

3 个答案:

答案 0 :(得分:3)

  

因为你不能用不同的不透明度来描边一条路径(如果我错了,请纠正我)

你错了=)

当您使用globalCompositeOperation = 'destination-out'lineDestinationOut)时,您需要将strokeStyle不透明度设置为1以删除所有内容。

但是,由于路径构建顺序的原因,只需在您的小提琴中更改它就不具备所需的效果。首先构建10%透明的整个长度,然后删除并绘制两个40%透明位。

Here's a jsfiddle of the code below

var canvas = document.getElementById('canvas');
var cx = canvas.getContext('2d');
cx.lineCap = 'round';
cx.lineJoin = 'round';
cx.lineWidth = 40;

// Create the first line, 10% transparency, the whole length of the shape.
cx.strokeStyle = 'rgba(0,0,255,0.1)';
cx.beginPath();
cx.moveTo(20,20);
cx.lineTo(260,20);
cx.lineTo(220,60);
cx.stroke();
cx.closePath();

// Create the first part of the second line, first by clearing the first
// line, then 40% transparency.
cx.strokeStyle = 'black';
cx.globalCompositeOperation = 'destination-out';
cx.beginPath();
cx.moveTo(20,20);
cx.lineTo(100,20);
cx.stroke();
cx.strokeStyle = 'rgba(0,0,255,0.4)';
cx.globalCompositeOperation = 'source-over';
cx.stroke();
cx.closePath();

// Create the second part of the second line, same as above.
cx.strokeStyle = 'black';
cx.globalCompositeOperation = 'destination-out';
cx.beginPath();
cx.moveTo(180,20);
cx.lineTo(260,20);
cx.stroke();
cx.strokeStyle = 'rgba(0,0,255,0.4)';
cx.globalCompositeOperation = 'source-over';
cx.stroke();
cx.closePath();

答案 1 :(得分:0)

<@> @ HenryBlyth的回答可能是你得到的最好的答案;没有本地API可以做你要求做的事情(在我看来,这有点奇怪......不透明度并不是假设来替换像素)。

在一个段落中拼出解决方案:将“笔画”拆分为具有不同不透明度的单个路径。正常绘制最低不透明度路径。然后,用“desitination-out”绘制较高的不透明度,以去除重叠的低不透明度路径。然后,像往常一样绘制高不透明度路径,使用“source-over”,以创建所需的效果。

正如对该答案的评论中所建议的那样,@ markE关于使每个路径成为在绘制之前预先排序的对象的评论是一个很好的建议。由于您希望执行本机API无法执行的手动绘制逻辑,将每个路径转换为对象并以这种方式处理它们比手动操作每个像素要容易得多(尽管该解决方案可行,但它也可以驱动您狂。)

你提到每个笔画都在另一个画布上完成,这很棒,因为你可以记录在绘制该线时触发的鼠标事件,创建一个表示该路径的对象,然后使用该对象和其他画布。您的“合并”画布,无需担心像素操作或其他任何问题。我强烈建议切换到像@markE建议的面向对象的方法,如果可能的话。

答案 2 :(得分:0)

使用两个图层绘制到:

  • 首先计算顶层不透明度40% - 10%并将其设置为顶层的alpha
  • 将底层设置为10%
  • 用虚线设置顶层(lineDash)(根据尺寸要求计算破折号图案大小)
  • 在两个图层上绘制线条,底层将是一条长线,顶层在描边时会在顶部绘制一条虚线。
  • 完成后将两个图层复制到主画布。