В KgBase мы недавно переписали пользовательский интерфейс графа, чтобы использовать WebGL вместо SVG. Нашей целью было:

  1. Повышение производительности и возможность визуализации 1000 узлов.
  2. Если возможно, продолжайте использовать React. Мы хотим быстро внедрять новые функции, а React позволяет быстрее создавать прототипы.

Очень распространенным инструментом для рендеринга содержимого WebGL является Pixi.js. Обычно он используется для создания простых игр, но очень хорошо работает в нашем случае.

Если вы хотите интегрировать Pixi.js с React, вам нужно будет использовать React Pixi (или react-pixi-fiber, но я вернусь к этому позже).

В своей первоначальной версии наш код рендеринга был таким простым:

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

Теперь обратите внимание на компонент Circle. Это не часть react-pixi. Мы импортируем это из нашей собственной ./Primitivesпапки.

React Pixi показывает вам, как создавать свои собственные компоненты, как указано выше, прямо на их целевой странице:

Заметили что-нибудь не так с этим кодом?

Я тоже этого не делал, пока мы не попали в конкретную ситуацию:

Когда вы нажимаете на узел, мы хотим его выделить. Мы делаем это, устанавливая альфа щелкнутого узла на 1.0, уменьшая альфа всех остальных узлов до 0.2 или около того.

В приведенной выше реализации каждое изменение альфа-канала будет вызывать applyProps, который перерисовывает всю фигуру. Итак, мы вызываем этот фрагмент 1000 раз каждый раз, когда пользователь щелкает узел:

Если мы просто хотим изменить альфа, это все, что нам нужно сделать:

Итак, правильный способ реализации вашего настраиваемого компонента Rectangle / Circle будет таким:

Важная часть - убедиться, что нет ненужного повторного рисования вашего компонента. Всякий раз, когда вы видите такой код:

Вам следует провести рефакторинг: всегда сравнивайте свой props с oldProps и выполняйте повторный рендеринг только в том случае, если что-то изменилось.

Интересный факт: реализация Text в react-pixi по умолчанию также будет обновлять компонент каждый раз, даже если ничего не изменилось. Реализация по умолчанию выглядит примерно так:

Установка нового текстового значения (instance.text = text) - дорогостоящая операция! Это приведет к повторной визуализации вашего экземпляра Pixi. Вместо этого всегда проверяйте необходимость повторного рендеринга.

Чтобы исправить это, вам нужно создать свой собственный компонент, например, с именем FastText:

И теперь рендеринг будет происходить только в том случае, если текст действительно был изменен.

При работе с Pixi и WebGL необходимо учитывать гораздо больше вопросов. То, что я описал выше, - хороший первый шаг к написанию высокопроизводительного кода. Всегда будьте осторожны с тем, что происходит в applyProps. Он никогда не должен создавать новые Graphics объекты, и он не должен даже изменять свойства без необходимости. Перед тем, как что-либо менять, всегда нужно сравнивать текущие и старые свойства.

Как я уже сказал, это хороший первый шаг. Благодаря этому подходу мы довольно легко смогли визуализировать до 5 000–10 000 узлов. Еще многое предстоит сделать для повышения FPS и большего количества узлов, но это тема для другого сообщения в блоге.