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