Странная задержка 700 мс при использовании context.drawImage (); - PullRequest
0 голосов
/ 01 июля 2019

Я работаю над небольшой анимацией холста, которая требует, чтобы я шагал по большому листу спрайта png, поэтому я получаю много пробега от drawImage (). Раньше у меня никогда не было проблем с его использованием, но сегодня я сталкиваюсь со странной задержкой блокировки после запуска drawImage.

Насколько я понимаю, drawImage является синхронным, но когда я запускаю этот код, drawImage сработало! происходит примерно за 700 мс до того, как изображение действительно появляется. Стоит отметить, что в Chrome 700 мс, а в Firefox - 1100 мс.

window.addEventListener('load', e => {
    console.log("page loaded");

    let canvas = document.getElementById('pcb');
    let context = canvas.getContext("2d");

    let img = new Image();

    img.onload = function() {
        context.drawImage(
            img,
            800, 0,
            800, 800,
            0, 0,
            800, 800
        );

        console.log("drawImage fired!");
    };

    img.src = "/i/sprite-comp.png";
});

enter image description here

В более широком контексте этот код выполняется в цикле requestAnimationFrame, и я испытываю эту задержку только во время первого выполнения drawImage.

Я думаю, что это связано с большим размером моего листа спрайта (28000 × 3200) @ 600 КБ, хотя событие onload, похоже, срабатывает правильно.

edit: вот распечатка времени (мс) между кадрами rAF. Я получаю этот результат последовательно, если не удалю функцию drawImage.

enter image description here

1 Ответ

2 голосов
/ 02 июля 2019

Это потому, что событие загрузки является только сетевым событием. Он только говорит о том, что браузер извлек медиа, проанализировал метаданные и обнаружил, что это действительный медиафайл, который он может декодировать.
Однако, часть рендеринга, возможно, все еще не была сделана, когда происходит это событие, и поэтому у вас есть первый рендеринг, который занимает так много времени. (Хотя раньше это было только поведение FF ..)

Поскольку да drawImage() является синхронным, то, таким образом, будет выполнено это декодирование + рендеринг синхронной операции. Это так верно, что вы даже можете использовать drawImage как способ сообщить , когда изображение действительно готово. .

Обратите внимание, что теперь в интерфейсе HTMLImageElement есть метод decode(), который точно расскажет нам об этом, неблокирующим способом, поэтому лучше использовать его, когда он доступен, и в любом случае. выполните прогрев всех ваших функций вне экрана, прежде чем запускать обширное графическое приложение.


Но так как ваше исходное изображение представляет собой спрайт-лист, вас может заинтересовать метод createImageBitmap () , который сгенерирует ImageBitmap из исходного изображения, при необходимости отрезать. Эти ImageBitmaps уже декодированы и могут быть нарисованы на холсте без задержки. Это должен быть ваш предпочтительный способ, так как это также позволит избежать рисования всего спрайт-листа каждый раз. А для браузеров, которые не поддерживают этот метод, вы можете исправить его, вернув HTMLCanvasElement с частью изображения, нарисованной на нем:

if (typeof window.createImageBitmap !== "function") {
  window.createImageBitmap = monkeyPatch;
}

var img = new Image();
img.crossOrigin = "anonymous";
img.src = "https://upload.wikimedia.org/wikipedia/commons/b/be/SpriteSheet.png";
img.onload = function() {
  makeSprites()
    .then(draw);
};


function makeSprites() {
  var coords = [],
    x, y;
  for (y = 0; y < 3; y++) {
    for (x = 0; x < 4; x++) {
      coords.push([x * 132, y * 97, 132, 97]);
    }
  }
  return Promise.all(coords.map(function(opts) {
      return createImageBitmap.apply(window, [img].concat(opts));
    })
  );
}

function draw(sprites) {
  var delay = 96;
  var current = 0,
    lastTime = performance.now(),
    ctx = document.getElementById('canvas').getContext('2d');
  anim();

  function anim(t) {
    requestAnimationFrame(anim);
    if (t - lastTime < delay) return;
    lastTime = t;
    current = (current + 1) % sprites.length;
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
    ctx.drawImage(sprites[current], 0, 0);
  }

}

function monkeyPatch(source, sx, sy, sw, sh) {
  return Promise.resolve()
    .then(drawImage);

  function drawImage() {
    var canvas = document.createElement('canvas');
    canvas.width = sw || source.naturalWidth || source.videoWidth || source.width;
    canvas.height = sh || source.naturalHeight || source.videoHeight || source.height;
    canvas.getContext('2d').drawImage(source,
      sx || 0, sy || 0, canvas.width, canvas.height,
      0, 0, canvas.width, canvas.height
    );
    return canvas;
  }
}
<canvas id="canvas" width="132" height="97"></canvas>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...