Оптимизация производительности цикла перерисовки HTML5 Canvas
Мы создаем приложение CAD, которое работает в браузере.
- CAD расшифровывается как Computer Aided Design.
- Illustrator, CorelDraw, AutoCAD и т. Д. Являются примерами приложений САПР.
Он основан на Paper.js, очень удобной библиотеке Canvas, которая позволяет программно управлять векторами.
Эта проблема
Основная проблема, с которой я столкнулся на данный момент, - это производительность цикла перерисовки.
Алгоритм перерисовки является "тупым" (с точки зрения умных хаков для повышения производительности) и, следовательно, неэффективным и медленным - рендеринг элементов графа сцены зависит от постепенно более медленного цикла перерисовки.
По мере накопления точек для рисования каждый цикл перерисовки становится все медленнее и медленнее.
Схема перерисовки проста:
- очистить всю область
- взять все предметы из графа сцены
- перерисовать все предметы.
Вопрос
Есть ли в классах примеры оптимизации визуализации в таких случаях - если предположить, что я хотел бы остановиться на реализации алгоритма "грязных прямоугольников" (отрисовка только областей, которые изменились)
Редактировать: я экспериментировал с ручной растеризацией на месте, которая работает довольно хорошо, я опубликовал ответ ниже.
2 ответа
Это может быть сделано с растеризацией в процессе / технике, подобной Bitmap Caching.
Проблема с графами сцен с большим числом узлов заключается в том, что их рендеринг вызывает стон механизма рендеринга. Браузер должен пройти их узлы и визуализировать их пиксели на холсте.
Итак, вот хорошее решение:
1. Рендеринг растрового изображения, но сохранить оригинальную форму ниже, скрытым
Решение состоит в том, чтобы заменить векторы изображениями, растеризуя их - только при рендеринге, но сохраняя исходную форму под копией изображения, в скрытом состоянии, только когда неактивен (внастоящее время не используется).
При нажатии на изображения - мы удаляем их и переключаем видимость исходной формы. Таким образом, неактивные фигуры визуализируются как изображения, а активные фигуры освобождаются от своего растрового представления и действуют как векторы, которые можно свободно манипулировать. Когда они не активны, они просто сидят там неуязвимыми с растровой копией на них.
Это позволяет механизму сохранять векторное представление форм, но избегает рендеринга их как векторов - вместо этого изображения, похожие на них, накладываются поверх них.
1000 команд пути по существу заменяются одним изображением - но только при рендеринге - исходный путь фактически существует как объект в графе сцены или в любом типе DOM, который вы используете
2. Растеризация в группах
Хитрость заключается в том, чтобы выполнить растеризацию в группах - сгруппируйте 10-15 фигур вместе и растеризуйте их как одно изображение. Это сохраняет количество растров на низком уровне. При нажатии на изображение - мы можем освободить всю группу или только элемент, по которому щелкнули.
3. Прикрепите обработчики щелчков к группе, чтобы восстановить векторную копию при повторной активации.
Растеризуя группу, мы можем просто прикрепить click
обработчик, поэтому при нажатии мы переключаем растровое изображение с вектором. Изображения не ведут себя так же, как векторы при тестировании попадания - изображения squares
по природе и не может быть ассиметрично проверен на удар. Хотя вектор считает, что его края находятся на границах пути, изображение считает, что его границы - это целая ограничительная рамка. Решение заключается в том, чтобы щелкнуть изображение, чтобы фактически проверить, щелкнуть точку щелчка с векторной траекторией под изображением - если оно возвращает значение true, то выполнить освобождение.
Полезный инструмент
Моя ветвь paper.js может помочь, но, возможно, она вам не подходит.
Это позволяет вам предотвратить paper.js перерисовывать все в каждом кадре (используйте paper.view.persistence = 1;
).
Таким образом, вы сможете лучше контролировать то, что нужно очистить, и должны быть перерисованы: например, когда вы перемещаете фигуру, вы можете очистить область, в которой она находилась (например, используя родной Canvas drawRect), и обновить ее после перемещения (используйте path.needsUpdate();
).
недостаток
Проблемы возникают, когда фигуры пересекаются. Если вы хотите изменить форму, которая пересекается с другой, вам придется обновить обе. То же самое, если вторая форма пересекает третью, и так далее, и так далее.
Таким образом, вам нужна рекурсивная функция, не сложная для кодирования, но она может быть дорогостоящей, если существует много пересекающихся сложных фигур, и в этом случае вы можете не получить производительности.
(Обновление) Растровое кеширование
Как предложил Nicholas Kyriakides в следующем ответе, растровое кэширование является очень хорошим решением.
Один холст на форму
Альтернативой было бы нарисовать каждую фигуру на отдельном холсте (работая как слои). Таким образом, вы можете свободно очищать и перерисовывать каждую фигуру независимо. Вы можете отключить событие onFrame от тех видов, которые не меняются (все холсты, кроме той, на которой работает пользователь). Это должно быть проще, но это приводит к другим небольшим проблемам, таким как совместное использование одних и тех же параметров вида проекта (в случае масштабирования), и это может быть дорогостоящим со многими формами (что означает много холста).
Статическое и динамическое полотно
(Возможно) лучшим подходом было бы иметь только два холста, один для статических фигур и один для активной формы. Холст статических фигур будет содержать все фигуры (кроме редактируемой) и будет перерисован только тогда, когда пользователь начнет и остановит редактирование активной фигуры. Когда пользователь начинает редактировать форму, она будет перенесена со статического холста на динамический, и наоборот, когда пользователь остановится.