Эта статья посвящена React Spring — библиотеке анимации на основе JavaScript. Мы рассмотрим её фичи, включая различные хуки и компоненты, и то, как использовать их в приложениях.

Анимация в React-приложениях постоянно развивается. Изначально она реализовывалась с помощью CSS-переходов, но с ростом сложности приложений стало ясно, что нужны более мощные инструменты. Появились библиотеки анимации на основе JavaScript, такие как Framer Motion, Remotion и React Spring, каждая из которых предлагает уникальные возможности для создания анимации в React.

Эта статья предполагает, что вы обладаете следующим:

  • есть базовое понимание React и React Hooks;

  • вы знакомы с синтаксисом JavaScript и JSX;

  • в среде разработки установлены Node.js и npm (или yarn);

  • есть редактор кода — например, Visual Studio Code.

Введение в React Spring

React Spring — это JavaScript-библиотека для создания интерактивных анимаций в React-приложениях. В отличие от традиционной анимации на основе CSS или других библиотек анимации React, React Spring использует анимацию на основе физики, которая имитирует реальные движения и создаёт более естественный эффект.

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

Настройка React Spring в проекте

Чтобы анимировать компоненты в React проекте с помощью React Spring, нужно выполнить следующие шаги:

  1. Скачать и установить библиотеку React Spring с помощью npm или yarn:

npm install react-spring
yarn add react-spring

Эти команды установят библиотеку react-spring и её зависимости в каталог проекта.

  1. После установки React Spring нужно импортировать необходимые компоненты и хуки в компоненты React, чтобы начать анимировать элементы. Это можно сделать с помощью следующего синтаксиса:

import { animated, (hook) } from  'react-spring'

В приведённом выше фрагменте кода мы импортируем две зависимости (хук и анимацию) из библиотеки React Spring. Вот подробное описание того, как работает каждый из них, и почему их необходимо импортировать.

Animated

В React Spring анимированное (animated) пространство имён предоставляет набор компонентов, которые позволяют анимировать элементы в React-приложении. Эти компоненты представляют анимированные версии стандартных HTML элементов, таких как <div>, <span> и <img>. Мы можем использовать эти анимированные элементы вместо обычных HTML элементов и применять к ним анимации с помощью хуков анимации React Spring.

Хуки

Для создания анимации в React-компонентах React Spring предлагает несколько хуков. Они упрощают процесс управления анимациями и позволяют легко интегрировать их в компоненты. Вот некоторые из них:

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

  • useTransition. Анимирует добавление, удаление или переупорядочивание элементов списка. Она управляет жизненным циклом анимации элементов, когда они входят или выходят из DOM, обеспечивая плавные переходы между различными состояниями списка.

  • useTrail. Используется для создания нескольких пружинных анимаций, создающих эффект «следа», когда каждая пружина следует за предыдущей или отстаёт от неё.

  • useChain. Как и цепочка, используется для определения последовательности анимаций с указанием порядка их следования.

  • useSprings. Хотя это похоже на useSpring, useSprings используется для управления несколькими пружинными анимациями одновременно, в то время как useSpring управляет одной пружинной анимацией.

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

Использование useSpring для создания анимаций

Хук useSpring в React Spring используется для создания анимации с использованием физики пружины. Он позволяет нам определить начальную и конечную точки анимации и использует свою библиотеку для обработки перехода между ними. Например:

const props = useSpring({ 
 opacity: 1,
  from: { opacity: 0 } 
  });

В этом примере мы создали функцию, которая изменяет непрозрачность элемента от 0 до 1. Эту функцию можно вызывать на разных элементах в зависимости от эффектов анимации. Давайте рассмотрим шаги, которые необходимо сделать при использовании хука useSpring для создания анимации.

Во-первых, импортируйте зависимости, необходимые для анимации:

import { useSpring, animated } from "react-spring";

Далее нам нужно определить компонент и использовать хук useSpring для создания анимированных значений. Хук useSpring принимает два основных аргумента:

  • Объект конфигурации. Он определяет свойства нашей анимации, включая:

    • from: начальное состояние анимированного значения (например, opacity: 0)

    • to: целевое состояние анимированного значения (например, opacity: 1)

    • config (необязательно): объект для точной настройки физического поведения пружины (например, масса, натяжение, трение).

  • Callback-функция (необязательно). Мы можем использовать функцию для создания динамической конфигурации на основе свойств или данных.

Анимацию useSpring можно создать двумя разными методами: с помощью объектного литерала и с помощью параметра функции.

Использование объектного литерала

Мы можем определить объект со свойствами, которые хотим анимировать, например, opacity или color и передать его в хук useSpring. Такой подход позволяет напрямую указать целевые значения для анимации.

Чтобы объяснить, как это работает, давайте создадим простой компонент, который анимирует непрозрачность элемента:

import React, { useState } from 'react';
import { useSpring, animated } from 'react-spring';

function App() {
  const [isVisible, setIsVisible] = useState(false);

  const opacityAnimation = useSpring({
    opacity: isVisible ? 1 : 0,
    config: {
      tension: 200, 
      friction: 20 
    }
  });

  const toggleVisibility = () => setIsVisible(!isVisible);

  return (
    <div>
      <button onClick={toggleVisibility} aria-label={isVisible ? 'Hide' : 'Show'}>
        {isVisible ? 'Hide' : 'Show'}
      </button>
      <animated.div style={opacityAnimation}>
        This text will fade in and out with spring physics.
      </animated.div>
    </div>
  );
}
export default App;

В этом фрагменте кода мы создаём кнопку, которая  переключает видимость некоторого текста. Для этого используются два хука — useState и useSpring.

С помощью useState проверяется, виден текст или нет, и создаётся анимация, изменяющая непрозрачность текста в зависимости от этого условия:

opacity: isVisible ? 1 : 0

Это даёт эффект анимации при нажатии на кнопку, вызывающую функцию toggleVisibility().

Использование параметра функции

В качестве альтернативы можно передать функцию в хук useSpring. Эта функция получает предыдущие значения анимации и возвращает объект с обновлёнными значениями для анимации. Это даёт больше контроля над тем, как анимация ведёт себя со временем:

const opacityConfig = {
    tension: 300,
    friction: 40,
  };

  // Define opacity animation with useSpring hook
  const opacityAnimation = useSpring(() => ({
    opacity: isVisible ? 1 : 0,
    config: opacityConfig,
  }));

При таком подходе конфигурация (натяжение и трение) извлекается в отдельный объект — opacityConfig, что обеспечивает большую гибкость для динамического управления на основе состояния или свойств.

Анимация элементов списка с помощью UseTransition

UseTransition — это хук React Spring, который анимирует элементы в массивах при их добавлении или удалении из DOM. Он особенно полезен для создания плавной анимации в списках; для этого он принимает список возможных конфигураций:

  • from определяет начальные стили для элементов, входящих в DOM.

  • enter задаёт стили для анимации при добавлении элементов. Мы можем создавать многоступенчатые анимации, предоставляя массив объектов.

  • leave задаёт стили, применяемые при удалении элементов из DOM.

  • update управляет тем, как анимировать изменения между существующими элементами.

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

  • from и to с переходами: их можно использовать в рамках enter, leave и update для более сложных анимаций с начальными и конечными состояниями, определяемыми независимо.

Чтобы проиллюстрировать работу useTransition, давайте создадим компонент, который добавляет и удаляет элементы из массива:

import React, { useState } from "react";
import { useTransition, animated } from "react-spring";

function App() {
  const [items, setItems] = useState([]);

  const addItem = () => {
    const newItem = `Item ${items.length + 1}`;
    setItems([...items, newItem]);
  };

  const removeItem = () => {
    if (items.length === 0) return;
    const newItems = items.slice(0, -1);
    setItems(newItems);
  };

  const transitions = useTransition(items, {
    from: { opacity: 0, transform: "translate3d(0, -40px, 0)" },
    enter: { opacity: 1, transform: "translate3d(0, 0, 0)" },
    leave: { opacity: 0, transform: "translate3d(0, -40px, 0)" },
  });

  return (
    <div className="transitionDiv">
      <div>
        <button onClick={addItem}>Add Item</button>
        <button onClick={removeItem}>Remove Item</button>
      </div>
      <div className="transitionItem">
        {transitions((style, item) => (
          <animated.div style={style} className ='list'>{item}</animated.div>
        ))}
      </div>
    </div>
  );
}

export default App;

В этом примере у нас есть компонент App, который управляет списком элементов. Он предоставляет кнопки для динамического добавления или удаления элементов из списка. При нажатии на кнопку “Add Item” («Добавить элемент) в массив добавляется новый элемент, а при нажатии на кнопку “Remove Item” («Удалить элемент») из массива удаляется последний элемент.

Хук useTransition используется для управления переходами элементов в массиве. Когда массив изменяется (в результате добавления или удаления элементов), useTransition обрабатывает анимацию для этих изменений в соответствии с заданной конфигурацией (определяемой свойствами from, enter и leave).

Массивы анимаций без изменений

Если в самом массиве нет динамических изменений, таких как добавление или удаление элементов, useTransition всё равно можно использовать для анимации каждого элемента в массиве. Например:

import { useTransition, animated } from "@react-spring/web";
import "./App.css";

const name = "Product1";
const name1 = "Product2";
const name2 = "Product3";

function App({ data = [name, name1, name2] }) {
  const transitions = useTransition(data, {
    from: { scale: 0 },
    enter: { scale: 1 },
    leave: { scale: 0.5 },
    config: { duration: 2500 },
  });

  return transitions((style, item) => (
    <div className="nameBody">
      <animated.div style={style} className="nameDiv">
        {item}
      </animated.div>
    </div>
  ));
}

export default App;

В этом примере компонент App отображает список элементов и применяет анимацию при каждой загрузке страницы.

Three vertical product circles grow from nothing
Three vertical product circles grow from nothing

Создание последовательных анимаций с помощью useTrail

Анимация useTrail используется для создания серии анимированных переходов для группы или списка элементов пользовательского интерфейса.

В отличие от традиционных методов анимации, которые анимируют элементы по отдельности, useTrail позволяет анимировать элементы один за другим, создавая тем самым эффект «следа» (“trail”). Обычно это используется при создании динамических списков, галерей изображений, переходов между страницами или любых других сценариев, где элементы должны анимироваться последовательно.

Вот основная структура синтаксиса:

const trail = useTrail(numberOfItems, config, [trailOptions]);

Давайте разберёмся:

  • NumberOfItems. Это необходимое число, которое определяет, сколько элементов мы хотим анимировать в trail.

  • config. Объект, определяющий свойства анимации для каждого элемента в trail. Каждый ключ в объекте представляет свойство анимации, и его значение может быть основано на нашей предполагаемой анимации. Например:

from: { opacity: 0, transform: 'translateX(50%)' },
to: { opacity: 1, transform: 'translateX(0)' },
transition: {
  duration: 500,
  easing: 'easeInOutCubic',
},
  • trailOptions (необязательно). Это массив дополнительных опций для trail. Некоторые общие опции:

    • trailKey: функция для генерации уникальных ключей для каждого элемента в trail (полезно для согласования с React).

    • reset: функция для сброса всех анимаций в trail.

Давайте посмотрим, как это работает:

import React, { useState, useEffect } from "react";
import { useTrail, animated } from "react-spring";

function App() {
  const [items, setItems] = useState([
    { id: 1, content: "This is a div illustrating a trail animation" },
    { id: 2, content: "This is a div illustrating a trail animation" },
    { id: 4, content: "This is a div illustrating a trail animation" },
    { id: 5, content: "This is a div illustrating a trail animation" },
  ]);
 []);

  const trail = useTrail(items.length, {

    from: { opacity: 1, transform: "translateY(0px)" },
    to: { opacity: 0, transform: "translateY(100px)" },
    delay: 400, // Add a delay between animations
    duration: 2000, // Customize the animation duration
  });

  return (
    <div className="container">
      {trail.map((props, index) => (
        <animated.div key={items[index].id} style={props} className="item">
          {items[index].content}
        </animated.div>
      ))}
    </div>
  );
}

export default App;

В приведённом выше фрагменте кода мы создаём компонент CardCarousel — он использует хук useTrail для анимирования каждой карточки карусели в зависимости от длины элементов в массиве.

const trail = useTrail(items.length, {

  from: { opacity: 1, transform: "translateY(0px)" },
  to: { opacity: 0, transform: "translateY(100px)" },
  delay: 400, // Add a delay between animations
  duration: 2000, // Customize the animation duration
});

Здесь задаются начальное и конечное состояние анимации (от и до), а также конфигурация перехода (длительность и смягчение), которая влияет на способ отображения анимации.

Рендеринг каждой карточки

Компонент возвращает <div> с классом card-carousel и отображает массив trail для рендеринга каждой анимированной карточки. Затем каждая карточка оборачивается в компонент animated.div с применением стилей анимации (непрозрачность и трансформация), заданных в хуке useTrail:

return (
    <div className="container">
      {trail.map((props, index) => (
        <animated.div key={items[index].id} style={props} className="item">
          {items[index].content}
        </animated.div>
      ))}
    </div>
  );

Подготовка последовательности анимаций с помощью useChain

В отличие от отдельных анимаций, useChain используется для связывания нескольких анимаций и задаёт последовательность выполнения заданных анимаций. Это особенно полезно при создании динамических пользовательских интерфейсов, где элементы анимируются один за другим.

Давайте рассмотрим синтаксис.

UseChain принимает массив ссылок на анимацию и необязательный объект конфигурации. Каждая ссылка на анимацию представляет собой отдельную анимацию, и они выполняются в том порядке, в котором появляются в массиве. Мы также можем указать время задержки для каждой анимации, чтобы контролировать время выполнения последовательности с помощью этого синтаксиса:

useChain([ref1, ref2, ref3], { delay: 200 });

Чтобы проиллюстрировать, как это работает, давайте создадим компонент, который применяет две анимации к разным элементам и управляет ими с помощью useChain:

import "./App.css";

import React, { useRef } from "react";
import {
  useTransition,
  useSpring,
  useChain,
  animated,
  useSpringRef,
} from "react-spring";

const data = ["", "", "", ""];

function App() {
  const springRef = useSpringRef();
  const springs = useSpring({
    ref: springRef,
    from: { size: "20%" },
    to: { size: "100%" },
    config: { duration: 2500 },
  });

  const transRef = useSpringRef();
  const transitions = useTransition(data, {
    ref: transRef,
    from: { scale: 0, backgroundColor: "pink" },
    enter: { scale: 1, backgroundColor: "plum" },
    leave: { scale: 0, color: "pink" },
    config: { duration: 3500 },
  });

  useChain([springRef, transRef]);

  return (
    <animated.div
      style={{
        display: "flex",
        alignItems: "center",
        justifyContent: "center",
        height: "400px",
        width: springs.size,
        background: "white",
      }}
    >
      {transitions((style, item) => (
        <animated.div
          style={{
            width: "200px",
            height: "200px",
            display: "flex",
            justifyContent: "center",
            alignItems: "center",
            textAlign: "center",
            marginLeft: "50px",
            color: "white",
            fontSize: "35px",
            borderRadius: "360px",
            ...style,
          }}
          className="products"
        >
          {item}
        </animated.div>
      ))}
    </animated.div>
  );
}

export default App;

В приведённом выше коде мы создаём две разные анимации, используя useString и useTransition, а также используем useChain для управления разными анимациями:

useChain([springRef, transRef]);

Создание нескольких анимаций с помощью хука useSprings

Как мы уже говорили, useSprings используется для создания нескольких пружинных анимаций одновременно, и каждая из этих анимаций имеет свои конфигурации. Это позволяет нам анимировать несколько элементов или свойств независимо друг от друга в рамках одного компонента. Например:

import { useSprings, animated } from "@react-spring/web";

function App() {
  const [springs, api] = useSprings(
    3,
    () => ({
      from: { scale: 0, color: "blue" },
      to: { scale: 1, color: "red" },
      config: { duration: 2500 },
    }),
    []
  );

  return (
    <div>
      {springs.map((props) => (
        <animated.div style={props} className="springsText">
          _______
        </animated.div>
      ))}
    </div>
  );
}

export default App;

В этом примере useSprings управляет массивом пружинных анимаций, каждая из которых представляет анимацию для одного элемента в массиве элементов. Каждый элемент в списке связан с конфигурацией spring, которая определяет начальные и целевые значения для свойств цвета и масштаба. Затем React Spring анимирует каждый элемент на основе соответствующей конфигурации.

Заключение

React Spring — это мощная библиотека анимации, которая позволяет создавать потрясающие интерактивные анимации в React-приложениях. Как мы увидели, эти анимации могут применяться к различным элементам в проектах.

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


Приглашаем всех желающих на открытый урок, на котором разберёмся, что такое прототипное наследование и как оно может помочь при разработке программ. В результате вы лучше поймёте объектную модель Javascript и сможете писать ООП код с экономией памяти. Записаться можно по ссылке.

Комментарии (0)