Описание изображения


Что такое Рив?

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

См. много интересных примеров в Общественная библиотека Рива.


Что мы можем оптимизировать?

Рив говорит нам, что все анимации уже работают отлично, и это так. Просто посмотрите этот тест от команды Rive, сравнивающий Lottie и Rive.

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


Есть куда расти

Использовать проект Rive в React очень просто. Следуя официальному руководству, вы можете сделать это так.

import Rive from '@rive-app/react-canvas';

export const Simple = () => <Rive src=" />;
Войти в полноэкранный режим

Выйти из полноэкранного режима

Как только компонент смонтирован, Rive загружает отдельный ВАСМ библиотека времени выполнения во время первоначальной инициализации. Rive использует эту библиотеку для управления анимацией и взаимодействия с ней. Я полагаю, что это также причина, по которой производительность Rive намного лучше по сравнению с альтернативными решениями, такими как Lottie. Этот модуль WASM не маленький и имеет размер 78 КБ.

Размер WASM

Однако в этом случае модуль WASM и файл анимации .riv начнут загружаться только после выполнения React, а если у вас есть анимация Rive в разделе Hero вашей страницы, она может появиться только через 4-5 секунд, если размер страницы достаточно велик. большой.

Учитывая, что вы используете современные фреймворки, которые дают вам SSG/SSR, исходный HTML придет к вам довольно быстро, но потом вы будете ждать гидратации React для инициализации анимации Rive.

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

React component mounted: ~2.5s
Rive animation playing: ~3.5s
Войти в полноэкранный режим

Выйти из полноэкранного режима

Это средние цифры. Они могут быть разными в зависимости от вашего местоположения и подключения. Тем не менее, очевидно, что есть возможности для улучшения. Моя цель состояла в том, чтобы сделать разницу между монтированием компонентов и воспроизведением анимации как можно короче, вот что вы можете сделать.


Разместите WASM самостоятельно

Если вы посмотрите на вкладку сети поближе, то увидите, что Rive не связывает модуль WASM с вашим проектом и всегда загружает его из

https://unpkg.com/@rive-app/canvas@1.0.64/rive.wasm
Войти в полноэкранный режим

Выйти из полноэкранного режима

Это создает дополнительное соединение, которого я хотел бы избежать, и обслуживать все файлы из одного и того же CDN. Чтобы исправить это, мы можем используйте следующий API:

import riveWasmUrl from '@rive-app/canvas/rive.wasm';
import { RuntimeLoader } from 'rive-react';

RuntimeLoader.setWasmUrl(riveWasmUrl);
Войти в полноэкранный режим

Выйти из полноэкранного режима

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

Итак, если вы используете Gatsby, сделайте следующее:

exports.onCreateWebpackConfig = ({ loaders, actions }) => {
  actions.setWebpackConfig({
    module: {
      rules: [
        {
          test: /\.wasm$/,
          use: [loaders.url()],
        },
      ],
    },
  });
};
Войти в полноэкранный режим

Выйти из полноэкранного режима

А это, если вы используете Next.js:

module.exports = {
  webpack: (config, options) => {
    config.module.rules.push({
      test: /\.wasm$/,
      use: ['url-loader'],
    });

    return config;
  },
};
Войти в полноэкранный режим

Выйти из полноэкранного режима

Сейчас стало лучше, но это не было самой большой проблемой здесь.


Предварительно загрузить WASM

Следующее, что мы можем сделать, это предварительно загрузить модуль WASM с помощью link rel="preload"

Поскольку мы уже пропатчили веб-пакет для загрузки WASM, теперь его очень просто предварительно загрузить. Добавьте его в основной макет, если вы используете Rive везде, или установите его в React Helmet на тех страницах, где он вам нужен.

import riveWasmUrl from '@rive-app/canvas/rive.wasm';

<link rel="preload" href={riveWasmUrl} as="fetch" crossOrigin="anonymous" />
Войти в полноэкранный режим

Выйти из полноэкранного режима


Предварительно загрузить анимацию .riv

То же самое вы можете сделать для .riv файл на странице, где он воспроизводится.

<link rel="preload" href="/path-to-rive-file/hero.riv" as="fetch" crossOrigin="anonymous" />
Войти в полноэкранный режим

Выйти из полноэкранного режима


Полученные результаты

С этим решением я смог добиться почти нулевой разницы между монтированием компонентов React и воспроизведением анимации Rive. Оставшаяся разница составила около 30 мс.

У меня также есть несколько других приемов, которые могут помочь вам в определенных условиях.


Ленивая загрузка Rive

Если вы используете небольшие анимации по всей странице, их легко загрузить заранее. Однако, если у вас есть сложная графика и анимация в Rive, это может легко занять 20 КБ+. По этой причине я рекомендую использовать Intersection Observer API, чтобы загружать их только тогда, когда они видны.

Чтобы достичь этого, нам нужно понять, как работает компонент Rive React. На основе исходного кода Rive он загружает .riv файл при монтировании компонента, а это означает, что для предотвращения загрузки дополнительных файлов нам необходимо предотвратить рендеринг <RiveComponent>.

Затем, используя react-intersection-observerмы можем подписаться на видимость раздела с анимацией.

И последний бит — обернуть RiveComponent с помощью <ImagePlaceholder> для предотвращения смещения макета на странице.

ImagePlaceholder — это простое решение, использующее заполнитель svg для сохранения необходимого соотношения сторон изображения. В качестве альтернативы можно использовать современный aspect-ratio Свойство CSS (которое, к сожалению, до сих пор не работает в Safari) или старая школа padding-bottom решение. Вы можете найти исходный код в демо или в нашем Веб-сайт репо.

Ниже приведен пример приложения, иллюстрирующий, как оно работает (Посмотреть демо)

import './styles.css';
import { useRive } from '@rive-app/react-canvas';
import { useInView } from 'react-intersection-observer';
import riveBotAnimationUrl from './bot.riv';
import ImagePlaceholder from './ImagePlaceholder';

const ART_BOARD = 'HeroBot';
const STATE_MACHINE_NAME = 'State Machine 1';

export default function App() {
  const { RiveComponent, rive } = useRive({
    src: riveBotAnimationUrl,
    autoplay: true,
    stateMachines: STATE_MACHINE_NAME,
    artboard: ART_BOARD,
  });

  const [ref, isInView] = useInView({
    triggerOnce: true,
    // rootMargin: "200px"
    threshold: 0.2,
  });

  return (
    <div className="App">
      <section className="hero">
        <div>
          <h1>Rive Lazy Loading Example</h1>
          <p>Scroll down the page and look at network tab</p>
        </div>
      </section>
      <section ref={ref} className="animation-container">
        <ImagePlaceholder height={400} width={400}>
          {isInView && <RiveComponent />}
        </ImagePlaceholder>
      </section>
    </div>
  );
}
Войти в полноэкранный режим

Выйти из полноэкранного режима

Примечание. Модуль Rive WASM также загружается автоматически при первой инициализации RiveComponent, поэтому при откладывании рендеринга вы также откладываете загрузку довольно большого времени выполнения.


Rive, React.lazy и Suspense

Последняя часть оптимизации, которая очень полезна, если вы вообще не хотите показывать анимацию Rive на определенных устройствах, — это использование React 18 Suspense и отложенная загрузка компонентов. Архитектура этого решения такова:

  1. Переместите компонент, который загружает, управляет и воспроизводит анимацию Rive, в отдельный компонент React.
  2. Импортируйте его, используя React.lazy. Это приведет к созданию отдельного пакета JS.
  3. Оберните ленивый загруженный компонент с помощью Suspense.
const RiveAnimation = lazy(() => import('components/rive-animation'))

const Section = () => {
return (
    {isMobile ? (
      <img src="my-example.png"/>
    ) : (
      <Suspense fallback={<Loading />}>
        <RiveAnimation />
      </Suspense>
    )}
  )
}
export default Section
Войти в полноэкранный режим

Выйти из полноэкранного режима

Вы можете найти полный пример этого решения в отличной демонстрации Пол Скэнлон.

Компонент Rive-анимации

Компонент раздела

Демонстрационный сайт


Вот и все!

Подытожим все об оптимальном использовании Rive с React:

  • Разместите библиотеку времени выполнения WASM самостоятельно. Это предотвратит дополнительное http-соединение с внешним сервером unpkg, на котором эта библиотека размещена по умолчанию.
  • Предварительно загрузите файлы .riv и WASM, если у вас есть анимация Rive в разделе «Герой» на странице. Это значительно сократит время загрузки и выполнения.
  • Создайте отдельный пакет для Rive, используя React lazy и Suspense, чтобы не загружать библиотеку при низкой скорости сети или на мобильных устройствах.

Если вам понравилась статья и вы хотите увидеть больше советов по веб-производительности, Подпишись на меня в Твиттере.