2017-10-19 87 views
0

我正面临一个明显已知的问题,其中HTML画布中的线条无法禁用抗锯齿(例如this question提到这只能使用context.imageSmoothingEnabled= false而不是用于线条的图像)。克服鼠标事件的画布线上的抗锯齿效果

我不知道是否有任何更新,如果有可能绘制一个清晰的(即使“像素化”)线?我所需要的就是当使用普通“铅笔”工具(参见下面的左侧行程)时,MS Paint可以实现,而不是更平滑的“刷子”类型(参见右侧行程)。

crisp pixel line in paint

目前,在我的HTML画布的线条看起来更像是“刷”的类型。 blurry canvas lines

这是一个问题,因为我正在尝试设置鼠标事件,让我行的确切的颜色,但与周围的实际颜色抗锯齿,它之前返回一些不想要的颜色(S)光标到达线的中心部分。

有人可以确认是否真的没有办法强制画布中的“铅笔”式中风?如果是这样,是否有一个简单的解决方法,例如“切断”中风的边缘?

回答

2

您可以使用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>

+0

巧妙使用SVG滤镜的 – Kaiido

+0

谢谢,这是相当不错的!但我仍然有两个问题。 1)在过滤画布上使用my mousemove事件时,此过滤器会导致以下错误:未捕获的DOMException:未能在CanvasRenderingContext2D上执行'getImageData':画布已被交叉源数据污染。是否可以避免这个交叉来源?也许通过加载SVG xml不同,然后使用'url(#crisp)'? 2)该线很脆,但不完全。是否可以参数化多少阿尔法过滤? – sc28

+0

我检查了Chrome通过使用SVG过滤器在输出图像上提高了安全性错误。 (FireFox没有问题)因此,您可以通过编辑像素值而不是SVG过滤器来删除Alpha值。 – defghi1977