我可以关闭HTML <canvas>元素上的抗锯齿功能吗?


85

我在玩<canvas>元素,画线等。

我注意到我的对角线是抗锯齿的。我希望自己的工作看上去比较粗糙-有什么办法可以关闭此功能?


我认为这与浏览器有关。也许有关您使用哪种软件的其他信息会有所帮助。
— Tomalak

我希望使用跨浏览器的方法,但是对任何单个浏览器都可以使用的方法对我来说仍然很有趣
— Blorgbeard将于

我只是想看看这个话题是否有任何变化?
— vternal3 2011年


有什么更新吗?
— 罗兰

Answers:


60

对于图像,现在有。context.imageSmoothingEnabled= false

但是,没有任何东西可以显式控制线条绘制。您可能需要使用和绘制自己的线条(困难的方式)。getImageDataputImageData


1
我对javascript行算法的性能感到疑惑。在某些时候,可能会让Bresenham受益。
— Blorgbeard将于

浏览器供应商最近都在吹捧新的超快速JS引擎,因此最终可以很好地利用它。
— Kornel

1
这真的有效吗?我正在使用绘制一条线,putImageData 但它仍然对附近该死的像素产生混淆。
— Pacerier '18

如果我先绘制到较小的画布(缓存),然后在关闭该设置的情况下将drawImage绘制到另一个画布,它将按预期工作吗?
— SparK

69

1-pixel在坐标上绘制线条,例如ctx.lineTo(10.5, 10.5)。在该点上绘制一条像素线(10, 10)意味着该1像素在该位置到达9.5,10.5从而在画布上绘制了两条线。

一个很好的技巧是0.5,如果您有很多单像素线,则不必总是将其添加到要绘制的实际坐标上,这是ctx.translate(0.5, 0.5)在开始时将其添加到整个画布上。


嗯,我很难摆脱使用这种技术的抗锯齿功能。也许,我想念一些东西吗?您介意在某个地方发布示例吗?
— 哈维

7
这并不能消除抗锯齿,但确实会使抗锯齿的线条看起来好多了,例如,当您实际需要一个像素时,消除那些令人讨厌的水平或垂直线条(两像素粗)。
— 大卫·吉恩

1
@porneL:不,在像素的角之间画线。当您的线宽为1像素时,则沿任一方向都将延伸半个像素
— Eric

加+0.5对我有效,但ctx.translate(0.5,0.5)没有。在FF39.0上
— Paulo Bueno

非常感谢你!我不敢相信我有1px的实际线条可以更改!
— 矮矮胖胖的大块

24

可以在Mozilla Firefox中完成。将此添加到您的代码:

contextXYZ.mozImageSmoothingEnabled = false;

在Opera中,当前是一项功能请求,但希望很快会添加。


凉。+1为您的贡献。我想知道是否禁用AA可以加快画线速度
— marcusklaas 2011年

7
OP希望取消抗锯齿线条,但这仅适用于图像。根据规范,它确定"whether pattern fills and the drawImage() method will attempt to smooth images if their pixels don't line up exactly with the display, when scaling images up"
— rvighne 2014年

14

它必须抗锯齿矢量图形

要正确绘制涉及非整数坐标(0.4,0.4)的矢量图形,需要使用抗锯齿功能,只有很少的客户端会这样做。

给定非整数坐标时,画布具有两个选项:

  • 抗锯齿-根据整数坐标与非整数坐标之间的距离(即舍入误差)来绘制坐标周围的像素。
  • 轮-应用一些舍入函数应用于所述非整数坐标(1.4,从而将成为1,例如)。

尽管对于较小的图形(半径为2的圆),曲线将显示清晰的步幅而不是平滑的曲线,但后者将适用于静态图形。

真正的问题是图形被平移(移动)时-一个像素与另一个像素之间的跳转(1.6 => 2、1.4 => 1),这意味着形状的原点可能会相对于父容器跳转(不断移动)上下左右1像素)。

一些技巧

提示1:您可以通过缩放画布(用x表示)来柔化(或强化)抗锯齿,然后自己将倒数比例(1 / x)应用于几何体(不使用画布)。

比较(不缩放):

几个矩形

与(画布比例:0.75;手动比例:1.33):

相同的矩形,边缘更柔和

和(画布比例:1.33;手动比例:0.75):

相同的矩形,边缘更暗

提示2:如果您确实想要锯齿状的外观,请尝试绘制每种形状几次(不删除)。每次绘制时,抗锯齿像素会变暗。

相比。绘制一次后:

几条路

绘制三次后:

相同的路径,但较暗,没有可见的抗锯齿。


@vanowm随时可以克隆并使用:github.com/Izhaki/gefri。所有图像都是来自/ demo文件夹的屏幕截图(对于技巧2略有修改的代码)。我相信您会发现将整数舍入到绘制的图形上很容易(花了我4分钟),然后拖动即可看到效果。
— 伊扎基


8

我想补充一点,在缩小图像和在画布上绘图时遇到了麻烦,尽管在放大时没有使用,但仍在使用平滑。

我解决了使用此:

function setpixelated(context){
    context['imageSmoothingEnabled'] = false;       /* standard */
    context['mozImageSmoothingEnabled'] = false;    /* Firefox */
    context['oImageSmoothingEnabled'] = false;      /* Opera */
    context['webkitImageSmoothingEnabled'] = false; /* Safari */
    context['msImageSmoothingEnabled'] = false;     /* IE */
}

您可以像下面这样使用此功能:

var canvas = document.getElementById('mycanvas')
setpixelated(canvas.getContext('2d'))

也许这对某人有用。


为什么不是context.imageSmoothingEnabled = false?
— 马丁·舍弗

在我写我的答案时,这不起作用。现在可以用吗?
— eri0o18年

1
它做到了,这是完全一样的,在javascript中,obj ['name']或obj.name一直是并且将永远是相同的,一个对象是一个命名值(元组)的集合,使用类似于哈希表,两种表示法将以相同的方式处理,根本没有理由您的代码以前不会工作过,最糟糕的是,它分配的值无效(因为它打算用于其他浏览器。一个简单的示例:write obj = {a:123}; console.log(obj ['a'] === obj.a?“是的”是“否” :)
— Martijn Scheffer

我以为您是说为什么要拥有其他所有内容,我的意思是,当时浏览器需要不同的属性。
— eri0o

好的,当然是:)我在谈论语法,而不是代码本身的有效性(它起作用)
— Martijn Scheffer

6
ctx.translate(0.5, 0.5);
ctx.lineWidth = .5;

使用此组合,我可以绘制漂亮的1px细线。


6
您不需要将lineWidth设置为.5……这将(或应该)使其半透明。
— aaaidan

4

注意一个非常有限的技巧。如果要创建2色图像,则可以在颜色#000000的背景上绘制颜色为#010101的任何形状。完成此操作后,您可以测试imageData.data []中的每个像素,并将其值设置为0xFF,而不是非0x00的值:

imageData = context2d.getImageData (0, 0, g.width, g.height);
for (i = 0; i != imageData.data.length; i ++) {
    if (imageData.data[i] != 0x00)
        imageData.data[i] = 0xFF;
}
context2d.putImageData (imageData, 0, 0);

结果将是非抗锯齿的黑白图片。由于将进行某些抗锯齿,因此这不是完美的,但是这种抗锯齿将非常有限,形状的颜色与背景的颜色非常相似。


1

对于那些仍在寻找答案的人。这是我的解决方案。

假设图像为1通道灰色。我只是在ctx.stroke()之后使用了阈值。

ctx.beginPath();
ctx.moveTo(some_x, some_y);
ctx.lineTo(some_x, some_y);
...
ctx.closePath();
ctx.fill();
ctx.stroke();

let image = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height)
for(let x=0; x < ctx.canvas.width; x++) {
  for(let y=0; y < ctx.canvas.height; y++) {
    if(image.data[x*image.height + y] < 128) {
      image.data[x*image.height + y] = 0;
    } else {
      image.data[x*image.height + y] = 255;
    }
  }
}

如果您的图像通道是3或4,则需要修改数组索引,例如

x*image.height*number_channel + y*number_channel + channel

0

关于StashOfCode的答案,只有两个注释:

  1. 它仅适用于灰度,不透明的画布(fillRect用白色,然后用黑色绘制,反之亦然)
  2. 细线(〜1px线宽)时可能会失败

最好改为这样做:

描边并填充#FFFFFF,然后执行以下操作:

imageData.data[i] = (imageData.data[i] >> 7) * 0xFF

这就解决了宽度为1px的线。

除此之外,StashOfCode的解决方案是完美的,因为它不需要编写自己的栅格化函数(不仅要考虑线条,还要考虑贝塞尔曲线,圆弧,带孔的填充多边形等)。


0

这是Bresenham算法在JavaScript中的基本实现。它基于此维基百科文章中描述的整数算术版本:https : //en.wikipedia.org/wiki/Bresenham%27s_line_algorithm

    function range(f=0, l) {
        var list = [];
        const lower = Math.min(f, l);
        const higher = Math.max(f, l);
        for (var i = lower; i <= higher; i++) {
            list.push(i);
        }
        return list;
    }

    //Don't ask me.
    //https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
    function bresenhamLinePoints(start, end) {

        let points = [];

        if(start.x === end.x) {
            return range(f=start.y, l=end.y)
                        .map(yIdx => {
                            return {x: start.x, y: yIdx};
                        });
        } else if (start.y === end.y) {
            return range(f=start.x, l=end.x)
                        .map(xIdx => {
                            return {x: xIdx, y: start.y};
                        });
        }

        let dx = Math.abs(end.x - start.x);
        let sx = start.x < end.x ? 1 : -1;
        let dy = -1*Math.abs(end.y - start.y);
        let sy = start.y < end.y ? 1 : - 1;
        let err = dx + dy;

        let currX = start.x;
        let currY = start.y;

        while(true) {
            points.push({x: currX, y: currY});
            if(currX === end.x && currY === end.y) break;
            let e2 = 2*err;
            if (e2 >= dy) {
                err += dy;
                currX += sx;
            }
            if(e2 <= dx) {
                err += dx;
                currY += sy;
            }
        }

        return points;

    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.