我可以关闭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.510.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.