Оптимизация производительности перерисовывания на холсте HTML5 Canvas

Мы создаем приложение САПР, которое работает в браузере.

Он основан на Paper.js, очень аккуратной библиотеке Canvas, которая позволяет вам программно манипулировать векторами.


Проблема

Основная проблема, с которой я сейчас сталкиваюсь, - это производительность цикла перерисовки.

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

Как накапливаются точки для рисования, каждый цикл перерисовывания становится медленнее и медленнее.

Схема перерисовки так же проста, как и она:

  • очистить всю область.
  • взять все элементы из графика сцены
  • перерисовать все элементы.

Вопрос

Есть ли в классе примеры оптимизации рендеринга в таких случаях - если я хочу прекратить реализацию алгоритма с грязными прямоугольниками (рисование только измененных областей)

Изменить: Я экспериментировал с ручной растеризацией на месте, которая работает очень хорошо, я разместил ответ ниже.

Ответы

Ответ 1

Это можно сделать с растеризацией в процессе/технике, подобном Bitmap Caching.

Проблема с высокими node -Count Scene Graphs заключается в том, что их рендеринг заставляет механизм рендеринга стонать. Браузер должен пересекать их узлы и отображать их пиксели на холсте.

Итак, хорошее решение:


1. Извлеките растровое изображение, но сохраните исходную фигуру ниже, скрытый

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

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

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

1000 команд пути по существу заменены одним изображением - но только при рендеринге - исходный путь фактически существует как объект на графике сцены или какой-либо тип DOM, который вы используете

2. Растеризация в группах

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

3. Прикрепите обработчики кликов в группе, чтобы восстановить копию вектора при повторной активации

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

Ответ 2

Полезный инструмент

Моя ветка paper.js может помочь, но это, возможно, не подходит для вас.

Это позволяет предотвратить перетаскивание paper.js всех кадров (используйте paper.view.persistence = 1;).

Таким образом, вы можете лучше контролировать то, что нужно очистить, и его нужно перерисовать: например, когда вы перемещаете фигуру, вы можете очистить область, где она была (например, с помощью собственного холста drawRect) и обновите его после его перемещения (используйте path.needsUpdate();).

Минус

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

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

(Обновление) Кэширование растровых изображений

Как было предложено Николаем Кириакидесом в следующем ответе, Bitmap-кэширование - очень хорошее решение.

Один холст на форму

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

Статический и динамический холст

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