React-three-fiber: повышаем производительность приложений

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

Бывает так, что ваше приложение обрабатывает графику и нуждается в высокой производительности – например, если это сервис для бронирования мест в зале, который мы показали в одной из прошлых статей. При этом зачастую нужны плавный зум или скроллинг элементов, а также поддержка различных библиотек. Рассмотрим, как сохранить производительность и скорость, на примере работы с библиотекой react-three-fiber.

React-three-fiber, можно с вами познакомиться?

Если вы еще не работали с react-three-fiber, то вероятно, слышали о библиотеках React и three.js. Указанная библиотека, в свою очередь, дает возможность использовать при работе с three.js подходы React, такие как virtual dom и jsx, при этом не снижая производительность готового приложения. 

При взаимодействии с react-three-fiber, как мы отметили выше, может возникнуть необходимость в плавной анимации большого количества элементов. В чем же трудность? 

Для начала необходимо задуматься о корне данной проблемы. Для того чтобы создать объект на сцене, мы используем geometry, material и оборачиваем их в mesh. Это выглядит вот так:

<mesh>
    <planeBufferGeometry attach="geometry" />
    <meshBasicMaterial attach="material" color="#0c60d8" />
</mesh>

Разобрав любой базовый пример использования библиотеки react-three-fiber, вы сможете реализовать что-то подобное. Однако, такой подход применим только в том случае, если вы анимируете небольшое количество элементов. Ограничение во многом зависит от вашего устройства, поэтому возьмем некоторое абстрактное значение. Допустим, n – максимальное число элементов, которые вы можете анимировать с частотой в 60 кадров в секунду.

А если ограничения вызывают негативные чувства?

Как же быть, если элементов больше, чем n? При таком подходе у вас будут возникать проблемы с производительностью, и это будет заметно, так как анимация станет не такой плавной, как мы задумывали. Тут возникает вопрос, как быть дальше. Неужели ничего нельзя сделать и нет выхода?

К счастью, это не так! 

Новый взгляд на вещи

Что же мешает нам? Основная проблема – это создание собственного mesh для каждого объекта на сцене. Из-за этого компьютеру приходится отображать по очереди все наши элементы, один за другим. Но давайте рассмотрим изображение как одну сущность, которая состоит из множества объектов. Представьте, вместо того, чтобы переносить с места на место предметы по отдельности, мы поместим их в какое-то “хранилище” и перенесем за раз. 

Именно такой подход и помогает решить проблему с производительностью. Единственное, что нас ограничивает в данной ситуации – для достижения результата  необходимо, чтобы все объекты использовали единые geometry и material. Но с таким ограничением можно мириться при решении множества задач.

Вместо тысячи слов

Давайте рассмотрим способы реализации подобного подхода. Для этих целей существует instancedMesh. Что это такое? По сути, это тот же mesh, только он состоит из множества объектов, которые имеют одни и те же geometry и material. Выглядит это вот так:

<instancedMesh
    ref={ref}
    args={[null, null, countOfElements]}
>
    <planeBufferGeometry attach="geometry" />
    <meshBasicMaterial attach="material" color="#0c60d8" />
</instancedMesh>

С виду обычный mesh, но есть ряд особенностей.

Во-первых, наличие ref тут неспроста. Именно с помощью него будет происходить добавление элементов в наш instancedMesh, так как на данном этапе внутри него нет ни одного элемента.

Во-вторых, countOfElements – это число элементов, которое будет добавлено в наш instancedMesh.

Ну и сам процесс добавления:

const tempObject = new THREE.Object3D();
 
coordinates.forEach(({x, y, id}) => {
  tempObject.position.set(x - width / 2, y - height / 2, -20);
 
  tempObject.updateMatrix();
 
  ref.current.setMatrixAt(id, tempObject.matrix);
});
 
ref.current.instanceMatrix.needsUpdate = true;

Давайте пройдем по шагам.

Мы создадим объект tempObject. Он будет нужен нам для формирования матрицы преобразований и всё. Мы берём оттуда матрицу, а объект отображать на сцене не будем.

Далее пройдем по нашему массиву элементов, которые послужат для создания объектов, добавляемых на сцену.

Для каждого элемента получим  его координаты x и y, а также уникальные идентификаторы id, которые  дадут нам возможность обращаться к элементам при необходимости.

Далее зададим позиционирование для объекта tempObject и обновим ему матрицу трансформаций. Такие трансформации, как смещение, поворот и тому подобные, можно записать в матричном виде и применить их к каждой из координат какого-либо элемента на сцене (подробнее с этим знакомит линейная алгебра).

Затем зададим матрицу для каждого из элементов, к которым планируем обращаться через id. Причём если объекта с данным идентификатором не существует, то он будет создан.

После добавления всех интересующих нас элементов обновим матрицу нашего instancedMesh и увидим изменения. Обратите внимание, что число элементов, которые мы добавляем в наш instancedMesh, должно быть равно тому значению, которое указали в countOfElements.

Подводя итоги

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

Спасибо за внимание! Надеемся, что этот опыт был вам полезен. 

Источник: https://habr.com/ru/company/simbirsoft/blog/595673/


Интересные статьи

Интересные статьи

Выгрузка пользователей из 1C ЗУП в Битрикс24 или правдивая история о том как настроить интеграцию 1С-Битрикс24 с ЗУП без 1С-ника В жизни так бывает, причём бывает чаще чем хотелось б...
Последние несколько месяцев для PHP разработчиков выдались поистине захватывающими. С релизами Composer 2.0 и PHP 8.0 произошли значительные обновления кодовой базы DXP (...
На сегодняшний день телефоны являются наиболее популярным устройством. По мировой статистике они занимают самый высокий показатель использования, в сравнении с десктопом и планшетом. П...
Если попытаться в двух словах описать, в чем заключается функция роутинга на фронтэнде веб-приложений, то можно придти к выводу, что каждый популярный фреймоворк совершенно по-разному представляе...
Компании растут и меняются. Если для небольшого бизнеса легко прогнозировать последствия любых изменений, то у крупного для такого предвидения — необходимо изучение деталей.