Prerender on canvas 2D

  • Tutorial

В попытках оптимизировать 2D анимацию созданную в canvas, был найден один интересный вариант.


hero image


Предварительная визуализация — prerender.


"А что если записать все кадры заранее и показать их после окончания анимации?" — подумали мы с товарищем и вот что получилось на следующий день.


Live demo


amedomary.ru/prerender-canvas


Code v1.0


Сама страница есть на Github.


Логика очень проста и реализуется в 2 шага:


Шаг 1 — Сохраняем каждый кадр нашей анимации в массив изображений


Шаг 1 - javascript код
  function renderLoop() {
    draw();

    // create prerender images
    canvasPrerender.toBlob(blob => {
      const reader = new FileReader();
      reader.readAsDataURL(blob);

      reader.onload = function() {
        const image = document.createElement("img");
        image.src = reader.result;
        prerenderArr.push(image);
      };
    });

    if (prerenderIsCompleted) {
      requestAnimationFrame(renderLoop);
    } else {
      requestAnimationFrame(drawPreImg);
    }
  }

Шаг 2 — После окончания — рисуем анимацию из уже готовых изображений


Шаг 2 - javascript код
  function drawPreImg() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.drawImage(prerenderArr[i], 0, 0);

    requestAnimationFrame(drawPreImg);
  }

Весь код целиком


Analytic


Конкретные значения индивидуальны и зависят от мощности машины. Я тестирую на macbook 13 2019 года.


Давайте посмотрим на код и затраты ресурсов в том числе и временных.


FPS


Fps "до" равен частоте кадров если бы мы запустили анимацию сразу без предварительной отрисовки.


Fps "до" запуска анимации (prerender): 14-22
Fps "после" (сама анимация): 58-60


fps counter

На скриншоте выше используется более сложное изображение, чем в live demo.


Время


Prerender — 9 секунд
Сама анимация — 3 секунды
Общее — 12 секунд (так же на live demo)


CPU / Performance


Как видно из скриншота ниже — наш процессор страдает только на этапе prerender'а. Нарисовать уже готовые изображения не составляет труда.


Производительность - скриншот

performance


Память


Для небольшой анимации как у нас всё это дело занимает:
~ 432 / 2 = 200 кадров
~ 25 мб


performance


Conclusion


Если у вас есть возможность подождать, прежде чем показать анимацию пользователю и она не будет съедать Гигабайты памяти — попробуйте сделать что то подобное.


P.S.


Если у вас есть идеи по повышению производительности данного подхода — пишите :)

Комментарии 6

    +2
    Забавно, раньше так во флэше анимации в битмапы кэшировали.
    А по теме — зачем так мучаться, когда есть WebGL. Примеров полно по запросу webgl pixel particles
      0
      Это уже второй вариант решения проблемы — но он более трудозатратный)
      Этот вариант я тоже проверю))
      +3

      Если анимация одна и та же, то можно использовать видео.

        –2
        Толково.
          0
          Если у вас есть идеи по повышению производительности данного подхода — пишите :)

          Когда плотно изучал канвас, запомнился такой хак для оптимизации:
          все тяжёлые расчёты и отрисовку делать в буферном канвасе (которого не видно в DOM). А потом уже покадрово выводить готовую картинку в настоящий канвас с помощью drawImage.
          Было правда давно, сейчас может и неактуально.
            0
            ну как показала практика — рисовать в буферном канвасе стоит так же как и в настоящем, хотя мб я не так использую буферный?

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое