画布fillPattern在Chrome上已偏移,但在Firefox上没有偏移

时间:2018-10-26 10:18:41

标签: javascript html5-canvas

我使用Path2D绘制了一个负轴路径,通过平移使其偏移,然后使用createPattern创建了一个渐变。我发现填充具有偏移量,并且Chrome浏览器上存在偏移量,这在Firefox浏览器中是正常的。为什么它们不同??


在Chrome和Firefox上打开此演示 https://codepen.io/xiechengjian/pen/vVbNXv

我的Chrome版本:70.0.3538.67 我的Firefox版本:63.0

let p = new Path2D();
ctx.translate(100,100)
p.moveTo(-100,-100)
p.lineTo(100,-100);
p.lineTo(100,100);
p.lineTo(-100,100);
p.closePath()
var offCanvas = document.createElement('canvas');
offCanvas.width = 200;
offCanvas.height = 200;
var offCtx=offCanvas.getContext("2d")

offCtx.fillStyle = "red"
offCtx.fillRect(0,0,200,200);
let gradient = ctx.createPattern(offCanvas, "no-repeat");
ctx.fillStyle = gradient
ctx.fill(p)
ctx.stroke(p)

1 个答案:

答案 0 :(得分:0)

This a Firefox bug.


首先,我想澄清一下我担心的误解。在与ctx.fill(p); ctx.stroke(p);一起使用之前,您创建的Path2D对象始终未连接到2D上下文。
仅在这些方法中,其子路径的坐标才会转换为上下文的当前矩阵。
因此ctx.translate(100,100);对Path2D对象没有影响,尽管对两个上下文的方法都有影响。

此外,您的Path2D实际上只是p.rect(-100, -100, 200, 200)。 因此,如果我们将所有这些Path2D代码替换为一个简单的ctx.rect()调用,就会发现您发现的差异与Path2D API无关:

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

ctx.fillStyle = generatePattern();
ctx.translate(100, 100);
ctx.rect(-100, -100, 200, 200);
ctx.fill();
ctx.stroke();

function generatePattern() {
  var offCanvas = document.createElement('canvas');
  offCanvas.width = 200;
  offCanvas.height = 200;
  var offCtx = offCanvas.getContext("2d");
  offCtx.fillStyle = "red";
  offCtx.fillRect(0, 0, 200, 200);
  return ctx.createPattern(offCanvas, "no-repeat");
}
<canvas id="canvas" width="500" height="250"></canvas>

那么发生的事情是fillStyle(和strokeStyle)属性就像一个无限大的层,它本身相对于上下文的转换矩阵。当您将其设置为CanvasPattern或CanvasGradient时,这很重要,因为位图的位置将由此转换矩阵确定。

这是一个简单的示例,显示了如何使用它来创建移动渐变,而子路径保持不变。

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
// we define the gradient only once
ctx.fillStyle = generateGradient();
// we define the sub-path only once
ctx.lineTo(20, 20);
ctx.lineTo(190, 50);
ctx.lineTo(130, 190);
ctx.closePath();

draw({clientX:50, clientY:50});
canvas.onmousemove = draw;

function draw(evt) {
  var rect = canvas.getBoundingClientRect();
  var x = evt.clientX - rect.left - 50;
  var y = evt.clientY - rect.top - 50;
  // clear
  ctx.setTransform(1,0,0,1,0,0);
  ctx.clearRect(0,0,canvas.width,canvas.height);
  // here, all that moves is the fillStyle layer
  ctx.translate(x, y);
  ctx.fill();
  ctx.stroke();
}


function generateGradient() {
  var grad = ctx.createRadialGradient(
    50,
    50,
    0,
    50,
    50,
    25
  ); // a circle whose center is at 50,50, and rad is 25
  grad.addColorStop(0, 'red');
  grad.addColorStop(1, 'green');
  return grad;
}
move your mouse over the triangle
<canvas id="canvas" width="500" height="250"></canvas>

在您的情况下,您创建的图案将在x和y轴上移动100px,因此 offCanvas 的左上角将在像素100,100处渲染,并且由于仅路径直到像素200,200为止,它将被裁剪。

但是Firefox为什么会像没有偏移一样呈现它?
这完全是Firefox如何呈现非重复CanvasPatterns的错误。
使用位图图像将使其显而易见:

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var img = new Image();
img.onload = function() {

  ctx.fillStyle = ctx.createPattern(img, "no-repeat");
  ctx.translate(100, 100);
  ctx.rect(-100, -100, 200, 200);
  ctx.fill();
  ctx.stroke();

};
img.src = 'https://mdn.mozillademos.org/files/222/Canvas_createpattern.png';
<canvas id="canvas" width="500" height="250"></canvas>

我的Firefox 64中的结果:
The image gets stretched in all directions

因此,当您使用纯红色作为图案时,此错误实际上会产生全红色的位图,而正确的行为according to specs是在图案算法未产生结果的情况下渲染透明的黑色像素。


要解决此问题,我们必须等待FF提供它。
但是请注意,在您的情况下,使用fillRect可以解决该错误。

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var img = new Image();
img.onload = function() {

  ctx.fillStyle = ctx.createPattern(img, "no-repeat");
  ctx.fillRect(0, 0, 200, 200);
  ctx.strokeRect(0, 0, 200, 200);

};
img.src = 'https://mdn.mozillademos.org/files/222/Canvas_createpattern.png';
<canvas id="canvas" width="500" height="250"></canvas>