Canvas fillPattern имеет смещение в Chrome, но не в Firefox

1

Я использовал Path2D для рисования пути отрицательной оси, смещения его с помощью translate, а затем создал градиент с 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)
Теги:
html5-canvas

1 ответ

0
Лучший ответ

Это ошибка Firefox.


Сначала я хотел бы прояснить, что я боюсь, это неправильное представление здесь. Созданный объект Path2D не подключен к вашему 2D-контексту так или иначе, прежде чем использовать его с ctx.fill(p); ctx.stroke(p); ctx.fill(p); ctx.stroke(p); ,
Только внутри этих методов координаты его подпутей будут преобразованы в контекстную текущую матрицу.
Таким образом, ctx.translate(100,100); не влияет на объект Path2D, но он будет иметь оба контекстных метода.

Кроме того, ваш Path2D на самом деле просто p.rect(-100, -100, 200, 200). Поэтому, если мы заменим весь этот код Path2D на простой ctx.rect(), мы увидим, что несоответствие, которое вы заметили, не связано с API Path2D:

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>

Итак, что происходит, это fillStylestrokeStyle) похоже на бесконечно большой слой, сам по себе относительно матрицы преобразования контекста. Когда вы устанавливаете его в 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>

В вашем случае созданный вами шаблон будет перемещаться на 100 пикселей на оси x и y, поэтому верхний левый угол вашего offCanvas будет отображаться на уровне 100 100 пикселей, и поскольку ваш Path будет показывать только до 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:
Изображение 174551

Поэтому, когда вы использовали сплошной красный цвет в качестве шаблона, эта ошибка фактически создавала полностью красную растровую карту, в то время как правильное поведение в соответствии с спецификациями - отображать прозрачные черные пиксели, где алгоритм шаблона не создавал никаких результатов.


Для исправления нам придется подождать, пока FF его предоставит.
Но учтите, что в вашем случае использование fillRect будет 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>
  • 0
    Спасибо за ваше терпение. Но на практике path2D, который мне нужно заполнить, содержит путь отрицательной оси и оси x, и y. поэтому я должен translate() чтобы нарисовать все пути. Я могу заполнить его с помощью createRadialGradient() или createLinearGradient() , но не с createPattern() В этом случае , Как я могу заполнить его с помощью createPattern() Спасибо снова ты
  • 0
    Как я уже объяснил, это ошибка браузера, которую нельзя исправить. Конечно, есть обходной путь, но это будет полностью зависеть от вашего конкретного случая использования. Я предлагаю вам написать новый вопрос, в котором вы бы указали точный случай (например, почему это должен быть неповторяющийся шаблон) в поисках обходного пути. Но сейчас я думаю, что дал ответ на вопрос «почему он отличается между Chrome и FF».
Показать ещё 4 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню