Створення інтерактивних анімацій за допомогою React Spring

React Spring

Ця стаття присвячена 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 . Хоча це схоже на useSpringuseSpringsвикористовується для управління кількома анімаціями пружинними одночасно, в той час як useSpringуправляє однією пружинною анімацією.

Щоб краще зрозуміти, як вони працюють, давайте розглянемо різні стилі анімації, яких можна досягти за допомогою кожного з цих хуків.

Використання useSpring для створення анімацій

Хук useSpringReact 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обробляє анімацію для цих змін відповідно до заданої конфігурації (визначеної властивостями fromenterі leave).

React Spring

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

Якщо в масиві немає динамічних змін, таких як додавання або видалення елементів, 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відображає список елементів та застосовує анімацію під час кожного завантаження сторінки.

React Spring

Створення послідовних анімацій за допомогою 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>
  );

React Spring

Підготовка послідовності анімацій за допомогою 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]);

React Spring

Створення декількох анімацій за допомогою хука 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 Spring – це потужна бібліотека анімації, яка дозволяє створювати чудові інтерактивні анімації в React-додатках. Як ми побачили, ці анімації можуть застосовуватись до різних елементів у проектах.

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

Переклад