0

问题

我正在尝试创建一个带有不透明度抖动的画笔工具(如在 Photoshop 中)。具体问题是:

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

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

结果应如下所示。所有代码和相应的输出都可以在这里找到(JSFiddle)。

因为你不能用不同的不透明度来描画一条路径(如果我错了,请纠正我)我的代码为每个段创建了一个具有不同不透明度的路径。

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

使用不透明像素时,变暗混合模式会产生所需的结果,但似乎不适用于透明度。失去透明度是一个交易破坏者。

使用不透明像素:

在此处输入图像描述

使用透明像素:

在此处输入图像描述

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

在绘制一个新的笔画段之前,使用“destination-out”合成操作符从下面的像素中减去它的不透明度。然后使用“source-over”添加新的笔画段。这几乎可以工作,但有点偏离。

在此处输入图像描述

寻找解决方案

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

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

3 回答 3

3

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

你错了=)

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

但是,由于路径构建的顺序,简单地在小提琴中更改它并没有所需的效果。首先构建 10% 透明的,整个长度,然后删除并绘制两个 40% 透明的位。

这是下面代码的jsfiddle

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();
于 2014-01-04T20:55:50.967 回答
1

使用两个图层绘制到:

  • 首先计算顶层不透明度 40% - 10% 并将其设置为顶层的 alpha
  • 将底层设置为 10%
  • 用虚线(lineDash)设置顶层(根据尺寸要求计算虚线图案尺寸)
  • 在两层上画线,底层将是一条长线,顶层在描边时会在顶部画一条虚线。
  • 完成后将两个图层复制到主画布。
于 2014-01-04T23:50:27.457 回答
0

@HenryBlyth 的答案可能是你能得到的最好的答案;没有本机 API 可以执行您被要求执行的操作(在我看来,无论如何这有点奇怪……不透明度真的不应该取代像素)。

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

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

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

于 2014-01-04T23:42:40.933 回答