克服鼠标事件的画布线上的抗锯齿效果
问题描述:
我正面临一个明显已知的问题,其中HTML画布中的线条无法禁用抗锯齿(例如this question提到这只能使用context.imageSmoothingEnabled= false
而不是用于线条的图像)。克服鼠标事件的画布线上的抗锯齿效果
我不知道是否有任何更新,如果有可能绘制一个清晰的(即使“像素化”)线?我所需要的就是当使用普通“铅笔”工具(参见下面的左侧行程)时,MS Paint可以实现,而不是更平滑的“刷子”类型(参见右侧行程)。
这是一个问题,因为我正在尝试设置鼠标事件,让我行的确切的颜色,但与周围的实际颜色抗锯齿,它之前返回一些不想要的颜色(S)光标到达线的中心部分。
有人可以确认是否真的没有办法强制画布中的“铅笔”式中风?如果是这样,是否有一个简单的解决方法,例如“切断”中风的边缘?
答
您可以使用SVG滤镜在画布上绘制这样的像素化形状,该滤镜可以去除形状边缘的Alpha值。 (但是,SVG过滤器是有点重。)
const ctx = canvas.getContext("2d");
//normal line(antialiased)
ctx.moveTo(20,20); ctx.lineTo(180,20);
ctx.stroke();
//set "crisp edge" filter
ctx.filter = "url(#crisp)";
//crisp line
ctx.beginPath();ctx.moveTo(20,40); ctx.lineTo(180,40);
ctx.moveTo(20, 50); ctx.lineTo(180, 70);
//crisp circle
ctx.moveTo(150, 130); ctx.arc(100, 130, 50, 0, Math.PI*2);
ctx.stroke();
<canvas id="canvas" width="200" height="200"></canvas>
<svg style="visibility:hidden;width:0;height:0;">
<defs>
<!--SVG filter to remove alpha-->
<filter id="crisp">
<feComponentTransfer>
<feFuncA type="discrete" tableValues="0,1"></feFuncA>
</feComponentTransfer>
</filter>
</defs>
</svg>
注:而Chrome使用SVG滤镜提高输出图像的安全错误。
如果你需要自己实现这样的过滤器,ImageData
对象将帮助你。这没有安全错误风险。
但手工制作的过滤器太重,无法应用于每个事件图纸。您应该仔细考虑适用时间。
const ctx = canvas.getContext("2d");
//temporary canvas to removeAlpha
const tmp = document.createElement("canvas");
[tmp.width, tmp.height] = [canvas.width, canvas.height];
const tctx = tmp.getContext("2d");
//normal line(antialiased)
ctx.moveTo(20,20); ctx.lineTo(180,20);
ctx.stroke();
//crisp line
tctx.beginPath(); tctx.moveTo(20,40); tctx.lineTo(180,40);
tctx.moveTo(20, 50); tctx.lineTo(180, 70);
removeAlpha();
//crisp circle
tctx.moveTo(150, 130); tctx.arc(100, 130, 50, 0, Math.PI*2);
tctx.stroke();
removeAlpha();
//remove alpha on tmp canvas and draw it to main canvas.
function removeAlpha(){
const threshold = 128;//you can change the value
const id = tctx.getImageData(0, 0, tmp.width, tmp.height);
const data = id.data;//pixel data array
//remove alpha values pixel by pixel.
for(let i = 0, len = canvas.width * canvas.height; i<len; i++){
const apos = i * 4 + 3;//alpha data position
const a = data[apos];
data[apos] = a > threshold ? 255 : 0;
}
tctx.putImageData(id, 0, 0);
ctx.drawImage(tmp, 0, 0);
tctx.clearRect(0, 0, tmp.width, tmp.height);
}
<canvas id="canvas" width="200" height="200"></canvas>
巧妙使用SVG滤镜的 – Kaiido
谢谢,这是相当不错的!但我仍然有两个问题。 1)在过滤画布上使用my mousemove事件时,此过滤器会导致以下错误:未捕获的DOMException:未能在CanvasRenderingContext2D上执行'getImageData':画布已被交叉源数据污染。是否可以避免这个交叉来源?也许通过加载SVG xml不同,然后使用'url(#crisp)'? 2)该线很脆,但不完全。是否可以参数化多少阿尔法过滤? – sc28
我检查了Chrome通过使用SVG过滤器在输出图像上提高了安全性错误。 (FireFox没有问题)因此,您可以通过编辑像素值而不是SVG过滤器来删除Alpha值。 – defghi1977