使用 drawImage() 时,Canvas 的跨浏览器像素网格不一致

2022-01-17 00:00:00 canvas javascript html html5-canvas

我认识到

您可能希望通过他们的 bug-tracker 让他们知道.

解决方法是确保您永远不会停留在浮动坐标上,即使在您的转换矩阵中也是如此.

I recognize that Canvas drawImage is inexplicably offset by 1 Pixel is a very similar issue, but I was already applying the advice given in that question's answer before I even came across this problem.

I'm implementing a sprite sheet system for an HTML5-based game. Individual frames are defined simply:

frame = new AnimationFrame(img, x, y, w, h);

Inside the AnimationFrame constructor, all of the numeric parameters are truncated to integers.

Then when I go to draw it on the canvas, I use what should also be simple code:

context.drawImage(frame.img,
  frame.x,
  frame.y,
  frame.w,
  frame.h,
  this.position.x | 0,
  this.position.y | 0,
  frame.w,
  frame.h,
);

Unfortunately, I get different results in different browsers.

In Chrome on Mac and Firefox on Mac, slicing the sprite sheet this way causes the individual frames to be offset by half a pixel, causing the top edge of the character's head to be drawn too thin and the top of the next sprite down to peek into the bottom of this one.

I can accommodate this problem with frame.x - 0.5 and frame.y - 0.5 but if I do that then I get the opposite problem in Safari on Mac and iOS and in Firefox on Windows.

I don't particularly WANT to do a browser detect to decide how to nudge the coordinate system, so I'm looking for suggestions for a way to either (1) force the various browsers to behave the same way, or (2) detect the issue at page load time with a test so I can just store the pixel grid offset in a variable.

NB: I'm going for a chunky pixel aesthetic, so my canvas is scaled by a factor of 2. This works fine without blurring, but it makes the half-pixel issue more clearly visible. Without scaling, the edges of the sprite still get distorted, so that's not the problem.

解决方案

I can't tell for IE, but at least for Safari, this sounds like a bug in their nearest-neighbor algorithm when the transformation matrix translate is set to exactly n.5.

onload = function() {

  var ctx = document.querySelector('canvas').getContext('2d');
  ctx.imageSmoothingEnabled = false;
  var img = document.querySelector('#hero');

  ctx.setTransform(2, 0, 0, 2, 99.49, 99.49);
  ctx.drawImage(img, 32, 32, 16, 16, 0, 0, 16, 16);

  ctx.setTransform(2, 0, 0, 2, 99.5, 99.5);
  ctx.drawImage(img, 32, 32, 16, 16, 16, 0, 16, 16);

  ctx.setTransform(2, 0, 0, 2, 99.51, 99.51);
  ctx.drawImage(img, 32, 32, 16, 16, 32, 0, 16, 16);

};

<img src='http://xmpps.greenmaw.com/~coda/html52d/hero.png' id='hero' style='display:none' />
<canvas width='200' height='200'></canvas>

Result in Safari 11.0.3

You may want to let them know about it from their bug-tracker.

The workaround would be to make sure that you never lay on floating coordinates, even in your transformation matrix.

相关文章