首页 > 解决方案 > Chrome94&95 画布错误

问题描述

Chrome 浏览器在 Canvas 绘图中有明显的 bug。以下代码

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
ctx.setTransform(2, 0, 0, 2, 0, 0);
ctx.beginPath();

const arr = [
      ["moveTo",7,209],
      ["lineTo",6318,403],
      ["lineTo",15786,453],
      ["lineTo",18942,451],
      ["lineTo",22098,28]
];
ctx.beginPath();
arr.forEach(item => {
      if (item[0] === 'lineTo') {
          ctx.lineTo(item[1], item[2]);
      } else if (item[0] === 'moveTo') {
          ctx.moveTo(item[1], item[2]);
      }
})
ctx.strokeStyle = 'red'
ctx.stroke();
<canvas
    id="canvas"
    width=800
    height=800
   style="width: 400px; height: 400px"
>
</canvas>

Chrome94 中的结果是这样的: Chrome94&95

Safari 中的相同代码是这样的: Safari

那么有没有办法避免chrome中的这个错误?

标签: javascripthtmlgoogle-chromecanvas

解决方案


此错误已在最新的 Canary 97 中修复。

这显然是您巨大坐标的舍入问题。所以为了避免它,如果可以的话,请避免使用那个大坐标。

否则,如果您确实必须使用解决方法,您可以强制画布使用软件渲染。但这会完全减慢画布的速度,应尽可能避免。

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d', {
  willReadFrequently: true // force software rendering (in 95+?)
});

ctx.setTransform(2, 0, 0, 2, 0, 0);
ctx.beginPath();

const arr = [
      ["moveTo",7,209],
      ["lineTo",6318,403],
      ["lineTo",15786,453],
      ["lineTo",18942,451],
      ["lineTo",22098,28]
];
ctx.beginPath();
arr.forEach(item => {
      if (item[0] === 'lineTo') {
          ctx.lineTo(item[1], item[2]);
      } else if (item[0] === 'moveTo') {
          ctx.moveTo(item[1], item[2]);
      }
})
ctx.strokeStyle = 'red';
// in case willReadFrequently didn't make it
if (ctx.getContextAttributes && !ctx.getContextAttributes().willReadFrequently) {
  ctx.getImageData(0, 0, 1, 1);
}
ctx.stroke();
<canvas
    id="canvas"
    width=800
    height=800
   style="width: 400px; height: 400px"
>
</canvas>

因此,最好的办法是尝试重现错误并事先检查您是否使用有错误的浏览器:

const isBuggyBrowser = (() => {
  const ctx = document.createElement("canvas").getContext('2d');
  // move the bug toward the top left corner
  ctx.setTransform(2, 0, 0, 2, -900, -900);
  // removed some fluff
  const arr = [
    [7,209],
    [6318,403],
    [15786,453],
    [18942,451],
    [22098,28]
  ];
  // lineTo from an empty subpath is auto-converted to moveTo
  arr.forEach(([x,y]) => ctx.lineTo(x, y));
  ctx.stroke();
  // not transparent
  return !!ctx.getImageData(0, 1, 1, 1).data[3]
})();
console.log( "is your browser buggy?", isBuggyBrowser );

现在您可以有条件地应用解决方法:

const isBuggyBrowser = (() => {
  const ctx = document.createElement("canvas").getContext('2d');
  // move the bug toward the top left corner
  ctx.setTransform(2, 0, 0, 2, -900, -900);
  // removed some fluff
  const arr = [
    [7,209],
    [6318,403],
    [15786,453],
    [18942,451],
    [22098,28]
  ];
  // lineTo from an empty subpath is auto-converted to moveTo
  arr.forEach(([x,y]) => ctx.lineTo(x, y));
  ctx.stroke();
  // not transparent
  return !!ctx.getImageData(0, 1, 1, 1).data[3]
})();

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d', {
  willReadFrequently: isBuggyBrowser
});

ctx.setTransform(2, 0, 0, 2, 0, 0);
ctx.beginPath();

const arr = [
      ["moveTo",7,209],
      ["lineTo",6318,403],
      ["lineTo",15786,453],
      ["lineTo",18942,451],
      ["lineTo",22098,28]
];
ctx.beginPath();
arr.forEach(item => {
      if (item[0] === 'lineTo') {
          ctx.lineTo(item[1], item[2]);
      } else if (item[0] === 'moveTo') {
          ctx.moveTo(item[1], item[2]);
      }
})
ctx.strokeStyle = 'red';
// in case willReadFrequently didn't make it
if (isBuggyBrowser && ctx.getContextAttributes &&
    !ctx.getContextAttributes().willReadFrequently) {
  ctx.getImageData(0, 0, 1, 1);
}
ctx.stroke();
<canvas
    id="canvas"
    width=800
    height=800
   style="width: 400px; height: 400px"
>
</canvas>


推荐阅读