Canvas drawImage - Firefox/Opera/IE浏览器(不包括Chrome)中瓦片的可见边缘

6

我正在canvas中绘制游戏地图。地面由瓷砖组成,是简单的64x64 png图像。

在Chrome中绘制时,看起来很好(左图),但在Firefox/Opera/IE中绘制时(右图),会出现明显的边缘: enter image description here

当我使用四舍五入的数字时,问题就消失了:

ctx.drawImage(img, parseInt(x), parseInt(y), w, h);

但是当我使用缩放时,这并没有帮助:

ctx.scale(scale); // anything from 0.1 to 2.0

我也尝试过这些方法,但没有任何改变:

  1. ctx.drawImage(img, 5, 5, 50, 50, x, y, w, h); // 所以不是夹紧的问题
  2. ctx.imageSmoothingEnabled = false;
  3. image-rendering: -moz-crisp-edges; (css)

有没有办法使它在Firefox/Opera/IE中工作?


编辑:已找到部分解决方案

将宽度/高度增加1像素,然后通过缩放进行补偿(width+1/scale)似乎有所帮助:

ctx.drawImage(props.img, 0, 0, width + 1/scale, height + 1/scale);

它会产生一些瑕疵,但我认为这是可以接受的。在这张图片上,你可以看到没有边缘的绿色瓷砖和未补偿的蓝色窗户,仍然有可见的边缘:

fixed

4个回答

3
最简单的解决方案(我认为也是最有效的)是在绘制游戏背景瓦片时使用有1像素重叠(比实际需要多1x1或2x2的大小)的瓦片。

不需要太复杂,只需稍微多画一点即可。这样可以避免引入额外的变换而带来的复杂性和性能考虑。

例如:

var img = new Image();
img.onload = function () {
    for (var x = 0.3; x < 200; x += 15) {
        for (var y = 0.3; y < 200; y += 15) {
            ctx.drawImage(img, 0, 0, 15, 15, x, y, 15, 15);
            // If we merely use 16x16 tiles instead,
            // this will never happen:
            //ctx.drawImage(img, 0, 0, 16, 16, x, y, 16, 16);
        }
    }
}
img.src = "http://upload.wikimedia.org/wikipedia/en/thumb/0/06/Neptune.jpg/100px-Neptune.jpg";

之前的代码: http://jsfiddle.net/d9MSV

之后的代码: http://jsfiddle.net/d9MSV/1/


注意,正如提问者所指出的,额外的像素需要考虑缩放,因此更正确的解决方案是他的修改:http://jsfiddle.net/d9MSV/3/


嗯,当我补偿不是1像素而是1/比例尺像素时,它似乎可以工作:http://jsfiddle.net/d9MSV/3/ - 我会在我的项目中尝试这个。如果它不会创建任何其他异常,那么它将解决我的问题。 - psycho brm
我已经更新了我的问题并提供了解决方案。如果你在回答中提到了缩放问题并附上了正确的jsfiddle链接,那么我想我可以接受它。因为你的帖子给了我灵感。 :) - psycho brm

1
在每个瓷砖绘制中,当涉及到除法时,请使用Math.floor,如下所示:
    ctx.drawImage(image,Math.floor(xpos/3),ypos+1)

此外,如果您有一个调用自身的循环来绘制,请始终使用requestAnimationFrame。我不知道为什么,但自从我从计时器超时改为使用requestAnimationFrame后,就再也没有出现过伪影。

Math.floor解决了我的问题! - functionptr

1

原因

这是由于反锯齿造成的。

Canvas仍在开发中,浏览器对处理反锯齿有不同的实现。

可能的解决方案

1

您可以尝试在Firefox中关闭图像的反锯齿功能,方法如下:

context.mozImageSmoothingEnabled = false;

在Chrome中:
context.webkitImageSmoothingEnabled = false;

并且像这样给元素添加一个类(应该可以在Opera中使用):

canvas {
    image-rendering: optimizeSpeed;             // Older versions of FF
    image-rendering: -moz-crisp-edges;          // FF 6.0+
    image-rendering: -webkit-optimize-contrast; // Webkit
    image-rendering: -o-crisp-edges;            // OS X & Windows Opera (12.02+)
    image-rendering: optimize-contrast;         // Possible future browsers.
    -ms-interpolation-mode: nearest-neighbor;   // IE
}

这是我做的一个浏览器测试,用来看关闭平滑字体渲染的效果:

平滑字体渲染浏览器测试

2

将整个画布向右下方移动0.5点。
ctx.translate(0.5, 0.5);

这种方法并不总是有效的,而且可能会与其他翻译产生冲突。不过,您可以每次添加一个固定的偏移量来解决问题:
ctx.translate(scrollX + 0.5, scrollY + 0.5);

3

另一个选择是做出妥协,你可以在瓷砖周围填充一个额外的像素,但我不推荐这样做,因为这会增加维护工作的负担。

4

这个方法会稍微缩放瓦片,以便它们重叠:

ctx.drawImage(tile, x, y, 65, 65); //source tile = 64x64

这可能足以解决故障。结合关闭抗锯齿(或使用最近邻)可减少对图块图形的影响,但缩放可能会稍微降低性能。如果关闭抗锯齿仍不起作用,则开销很小,因为某些内容将用于插值图像。

5

只需将所有内容向左偏移1个位置绘制(即网格为63x63)。当然,这将会破坏涉及检查的所有其他内容...


  1. 没有影响。
  2. 也不行(正如我所说,我使用ctx.scale(),因此像素对齐是不可能的)。
  3. 不是这样的,每个瓦片都是单独的file.png文件。
  4. 对于比例大于1的情况有效,但对于比例小于1的情况无效。
  5. 和4一样的结果。
- psycho brm

0

我将所有的瓷砖都绘制到一个完美大小的缓冲区中,然后使用drawImage将该缓冲区绘制到显示画布上,该函数会自动进行缩放。如果你有16x16的瓷砖,请将你的缓冲区设置为16的倍数,例如256x128或64x96等。这样可以消除由于绘制缩放尺寸而产生的瓷砖之间的空隙。唯一的缺点是你必须两次绘制完整的背景:一次在像素完美的空间中绘制背景,一次在最终的显示画布上绘制缩放后的图像。记得保持缓冲区和显示画布之间的纵横比以避免扭曲最终的图像。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接