Реклама
Перетяжка // Коробка 3.0

5 современных способов создания анимации в React

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

Обложка: 5 современных способов создания анимации в React

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

Давайте поговорим о них.

Примечание: весь код из статьи вы можете найти в этом репозитории.

1. Метод CSS

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

Давайте посмотрим на пример гамбургер-меню:

5 современных способов создания анимации в React_6

Это простое меню со свойством css и триггером className = "is-nav-open" для тега html. Способов реализации много. Один из них — создать wrapper над навигацией и вызывать изменения полей. Навигация имеет постоянную ширину, равную 250px, и wrapper со свойством margin-left или translateX с той же шириной. Если нужно показать навигацию, следует добавить className = "is-nav-open" для wrapper и переместить wrapper на margin-left / translateX: 0;

			import React, {Component} from 'react';
import {Icon} from 'antd';
import './style.css';

export default class ExampleCss extends Component {
    constructor(props){
        super(props);
        this.wrapperRef = React.createRef();
    }
    handleClick() {
        const wrapper = this.wrapperRef.current;
        wrapper.classList.toggle('is-nav-open')
    }
    render() {
        return (
            <div ref={this.wrapperRef} className="wrapper">
                <div className="nav">
                    <icon
                        className="nav__icon"
                        type="menu-fold"
                        onClick={() => this.handleClick()}/>
                    <div className="nav__body">
                        Lorem ipsum dolor sit amet, consectetur adipisicing elit.
                        Beatae ducimus est laudantium libero nam optio repellat
                        sit unde voluptatum?
                    </div>
                </div>
            </div>
        );
    }
}
		

И CSS стили:

			.wrapper {
    display: flex;
    width: 100%;
    height: 100%;
    transition: margin .5s;
    margin: 0 0 0 -250px;
}

.wrapper.is-nav-open {
    margin-left: 0;
}

.nav {
    position: relative;
    width: 250px;
    height: 100%;
    padding: 20px;
    border-right: 1px solid #ccc;
}
.nav__icon {
    position: absolute;
    top: 0;
    right: -60px;
    padding: 20px;
    font-size: 20px;
    cursor: pointer;
    transition: color .3s;
}

.nav__icon:hover {
    color: #5eb2ff;
}
		

Этот метод необходим в большинстве случаев. Лучше написать несколько строк CSS и изменять className, чем импортировать и использовать большие библиотеки. А пользователи будут благодарны за то, что приложение быстро работает в браузере.

2. ReactTransitionGroup

Этот дополнительный компонент был разработан парнями из сообщества ReactJs. ReactTransitionGroup легко реализует базовые CSS-анимации и переходы.

Разработчики описывают эту библиотеку так:

Набор компонентов для управления состояниями с течением времени, специально разработанный для анимаций.

Три вещи, которые вы должны знать об этом компоненте:

  1. ReactTransitionGroup меняет классы при изменении жизненного цикла компонента. Анимированный стиль, в свою очередь, должен быть описан в классах CSS.
  2. ReactTransitionGroup имеет небольшой размер. Он должен быть установлен в пакете для React приложения и несущественно увеличит вашу сборку. Также можно использовать CDN.
  3. ReactTransitionGroup имеет 3 компонента (Transition, CSSTransition и TransitionGroup). Чтобы запустить анимацию, нужно обернуть компонент в них.

Посмотрим, как сделать похожую анимацию.

5 современных способов создания анимации в React_20

Сначала нужно импортировать CSSTransitionGroup из react-transition-group. Затем вы должны обернуть список и установить свойство transitionName. Каждый раз, когда добавляется или удаляется дочерний элемент в CSSTransitionGroup, он получает стили анимации.

			<CSSTransitionGroup
    transitionName="example"
>
    {items}
</CSSTransitionGroup>
		

Если вы установили свойство transitionName = "example", классы в таблицах стилей должны начинаться с имени примера.

			.example-enter {
    opacity: 0.01;
}

.example-enter.example-enter-active {
    opacity: 1;
    transition: opacity 300ms ease-in;
}

.example-leave {
    opacity: 1;
}

.example-leave.example-leave-active {
    opacity: 0.01;
    transition: opacity 300ms ease-in;
}
		

Вы можете увидеть базовое использование ReactTransitionGroup.

Конечно, надо добавить немного логики. Следует описать два метода для реализации нашего примера списка контактов:

handleAdd — добавляет новые контакты, получает случайное имя и помещает его в массив state.items. (для случайного имени используется пакет random-name).

handleRemove — удаление контакта по индексу в массиве state.items.

			import React, { Component, Fragment } from 'react';
import { CSSTransitionGroup } from 'react-transition-group'
import random from 'random-name'
import Button from './button'
import Item from './item'
import './style.css';

export default class ReactTransitionGroup extends Component {
    
    constructor(props) {
        super(props);
        this.state = { items: ['Natividad Steen']};
        this.handleAdd = this.handleAdd.bind(this);
    }

    handleAdd() {
        let newItems = this.state.items;
        newItems.push(random());
        this.setState({ items: newItems });
    }

    handleRemove(i){
        let newItems = this.state.items.slice();
        newItems.splice(i,1);
        this.setState({items: newItems});
    }

    render () {
        const items = this.state.items.map((item, i) => (
            <Item
            item={item}
            key={i}
            keyDelete={i}
            handleRemove={(i) => this.handleRemove(i)}
            />
        ));

        return (
            <Fragment>
                <Button onClick={this.handleAdd}/>
                    <div className="project">
                        <CSSTransitionGroup
                        transitionName="example"
                        transitionEnterTimeout={500}
                        transitionLeaveTimeout={300}
                        >
                            {items}
                        </CSSTransitionGroup>
                    </div>
            </Fragment>
        );
    }
};
		

3️. React-animations

React-animations — библиотека, построенная на анимациях из animate.css. Она проста в использовании и имеет множество коллекций анимации. React-animation работает с любой inline-style библиотекой, которая поддерживает использование объектов для определения ключевых кадров анимации, таких как Radium, Aphrodite или styled-components.

Можете посмотреть на анимации в гифке:

5 современных способов создания анимации в React_33

 

Давайте посмотрим, как это работает. Например, анимация подпрыгивания.

5 современных способов создания анимации в React_36

Сначала нужно импортировать выбранную анимацию из react-animations.

			const Bounce = styled.div`animation: 2s ${keyframes`${bounce}`} infinite`;
		

Когда компонент создан, нужно обернуть любой HTML-код или компонент для анимации.

			<bounce><h1>Hello Animation Bounce</h1></bounce>
		

Пример:

			import React, { Component } from 'react';
import styled, { keyframes } from 'styled-components';
import { bounce } from 'react-animations';
import './style.css';

const Bounce = styled.div`animation: 2s ${keyframes`${bounce}`} infinite`;

export default class ReactAnimations extends Component {
    render() {
        return (
            <Bounce><h1>Hello Animation Bounce</h1></bounce>
        );
    }
}
		

Анимация работает. Она базовая и очень простая.

Есть хорошее решение для использования этой анимации при прокрутке — react-animate-on-scroll.

4️. React-reveal

React Reveal — это анимационный фреймворк для React. В нём есть основные анимации, такие как постепенное исчезновение, отражение, масштабирование, вращение и другие более сложные анимации. Он позволяет управлять всеми анимациями с помощью props, например, задавать дополнительные настройки: положение, задержка, расстояние, каскад и многие другие. Можно увидеть их здесь. Также можно использовать кастомные эффекты CSS, серверный рендеринг и компоненты высокого порядка. Если вы предпочитаете использовать анимацию прокрутки — этот фреймворк для вас.

			import Fade from 'react-reveal/Fade';

<Fade top>
    <h1>Title</h1>
</Fade>
		

Давайте посмотрим на эту анимацию прокрутки.

5 современных способов создания анимации в React_49

У нас есть 5 блоков, у каждого из которых полноэкранная страница и заголовок внутри.

			import React, { Component, Fragment } from 'react';
import Fade from 'react-reveal/Fade';

const animateList = [1, 2, 3, 4, 5];

export default class ReactReveal extends Component {
    render() {
        return (
            <Fragment>
                {animateList.map((item, key) => (
                    <div style={styles.block} key={key}>
                        <Fade top>
                            <h1 style={styles.title}>{`block ${item}`}</h1>                       
                        </Fade>
                    </div>
                ))}
            </Fragment>
        );
    }
}

const styles = {
    block: {
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        width: '100%',
        height: '100%',
        background: '#000',
        borderBottom: '1px solid rgba(255,255,255,.2)',
    },
    title: {
        textAlign: 'center',
        fontSize: 100,
        color: '#fff',
        fontFamily: 'Lato, sans-serif',
        fontWeight: 100,
    },
};
		

Мы создаём константу animateList. Этот массив содержит 5 элементов. После использования метода массива map вы можете рендерить каждый элемент в компонентах Fade и вставить элементы в заголовок. Стили, определённые в константе styles, имеют короткие стили CSS для блока и заголовка. У нас есть 5 блоков с анимацией Fade сверху.

5️. TweenOne и анимация в Ant Design

Ant Design — это библиотека React UI, представляющая собой множество простых в использовании компонентов. Это полезный компонент для создания элегантных пользовательских интерфейсов. Ant Design создан китайским конгломератом Alibaba, который использует его во многих своих проектах.

Давайте рассмотрим анимацию на их лендинге.

5 современных способов создания анимации в React_56

Как видите, здесь много анимированных элементов. Рассмотрим короткую версию, потому что у всех элементов одинаковая анимация. Наша анимация будет выглядеть так:

5 современных способов создания анимации в React_58

В этой анимации использован компонент TweenOne, но ему нужен PathPlugin для задания траектории. Это будет работать, когда вы поместите PathPlugin в TweenOne.plugins.

			TweenOne.plugins.push(PathPlugin);
		

Основные параметры анимации:

  • duration — время анимации в мс;
  • ease — плавность анимации;
  • yoyo — чередование движения вперёд и назад с каждым повторением;
  • repeat — повтор анимации. Нужно использовать -1 для бесконечной анимации;
  • p — координаты пути для анимации;
  • easePath — координаты плавного пути для анимации.

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

			const duration = 7000;
const ease = 'easeInOutSine';
const p =
  'M123.5,89.5 C148,82.5 239.5,48.5 230,17.5 C220.5,-13.5 127,6 99.5,13.5 C72,21 -9.5,56.5 1.5,84.5 C12.5,112.5 99,96.5 123.5,89.5 Z';
const easePath =
  'M0,100 C7.33333333,89 14.3333333,81.6666667 21,78 C25.3601456,75.6019199 29.8706084,72.9026327 33,70 C37.0478723,66.2454406 39.3980801,62.0758689 42.5,57 C48,46.5 61.5,32.5 70,28 C77.5,23.5 81.5,20 86.5,16 C89.8333333,13.3333333 94.3333333,8 100,0';
const loop = {
  yoyo: true,
  repeat: -1,
  duration,
  ease,
};
		

Далее создадим объект анимации. Этот объект имеет 3 типа анимации:

  • redSquare — в нём есть параметры цикла, а также координата Y, длительность и задержка;
  • greenBall — имеет путь с параметрами объекта x, y — значение p. Длительность, повтор и плавность — TweenOne.easing.path — функция с двумя параметрами:path — координата easePath;lengthPixel — кривая, разделённая на 400 секций;
  • track — овал с осями, который имеет стили цикла и параметр поворота.
			const animate = {
  redSquare: {
    ...loop,
    y: 15,
    duration: 3000,
    delay: 200,
  },
  greenBall: {
    path: { x: p, y: p },
    duration: 5000,
    repeat: -1,
    ease: TweenOne.easing.path(easePath, { lengthPixel: 400 }),
  },
  track: {
    ...loop,
    rotate: 15,
  },
};
		

Обратите внимание на компонент TweenOne. Напомним вкратце, эти компоненты будут импортированы из rc-tween-one. Он используется в качестве базового компонента с базовыми props и анимационными props. Это и есть наша анимация. Каждый TweenOne имеет свои собственные правила анимации, такие как redSquare, track, greenBall.

			import React from 'react';
import TweenOne from 'rc-tween-one';

export default function BannerImage() {
    return (
      <div className="wrapper-ant-design">
        <svg width="482px" height="500px" viewBox="0 0 482 500">
          <defs>
            <path
              d="M151,55 C129.666667,62.6666667 116,74.3333333 110,90 C104,105.666667 103,118.5 107,128.5 L225.5,96 C219.833333,79 209.666667,67 195,60 C180.333333,53 165.666667,51.3333333 151,55 L137,0 L306.5,6.5 L306.5,156 L227,187.5 L61.5,191 C4.5,175 -12.6666667,147.833333 10,109.5 C32.6666667,71.1666667 75,34.6666667 137,0 L151,55 Z"
              id="mask"
            />
          </defs>
          <g stroke="none" strokeWidth="1" fill="none" fillRule="evenodd" transform="translate(0, 30)">
            <g id="Group-13" transform="translate(0.000000, 41.000000)">
              <TweenOne component="g" animation={animate.redSquare}>
                <rect
                  stroke="#F5222D"
                  strokeWidth="1.6"
                  transform="translate(184.000000, 18.000000) rotate(8.000000) translate(-184.000000, -18.000000) "
                  x="176.8"
                  y="150.8"
                  width="14.4"
                  height="14.4"
                  rx="3.6"
                />
              </TweenOne>
            </g>
            <g id="Group-14" transform="translate(150.000000, 230.000000)">
              <g id="Group-22" transform="translate(62.000000, 7.000000)">
                <image
                  id="cc4"
                  alt="globe"
                  xlinkHref="https://gw.alipayobjects.com/zos/rmsportal/FpKOqFadwoFFIZFExjaf.png"
                  width="151px"
                  height="234px"
                />
              </g>
              <mask id="mask-2">
                <use xlinkHref="#mask" fill="white" transform="translate(-42, -33)" />
              </mask>
              <g mask="url(#mask-2)">
                <TweenOne component="g" animation={animate.track} style={{ transformOrigin: '122.7px 58px' }}>
                  <g transform="translate(-16, -52)">
                    <g transform="translate(16, 52)">
                      <path
                        d="M83.1700911,35.9320015 C63.5256194,37.9279025 44.419492,43.1766434 25.8517088,51.6782243 C14.3939956,57.7126276 7.77167019,64.8449292 7.77167019,72.4866248 C7.77167019,94.1920145 61.1993389,111.787709 127.105708,111.787709 C193.012078,111.787709 246.439746,94.1920145 246.439746,72.4866248 C246.439746,55.2822262 212.872939,40.6598106 166.13127,35.3351955"
                        id="line-s"
                        stroke="#0D1A26"
                        strokeWidth="1.35"
                        strokeLinecap="round"
                        transform="translate(127.105708, 73.561453) rotate(-16.000000) translate(-127.105708, -73.561453) "
                      />
                    </g>
                    <TweenOne component="g" animation={animate.greenBall}>
                      <image
                        alt="globe"
                        id="id2"
                        xlinkHref="https://gw.alipayobjects.com/zos/rmsportal/IauKICnGjGnotJBEyCRK.png"
                        x="16"
                        y="62"
                        width="26px"
                        height="26px"
                      />
                    </TweenOne>
                  </g>
                </TweenOne>
              </g>
            </g>
          </g>
        </svg>
      </div>
    );
  }
		
5 современных способов создания анимации в React_70

Выглядит пугающе. Но на самом деле нужно обратить внимание на эти строки.

			<TweenOne component="g" animation={animate.redSquare} />
  <TweenOne component="g" animation={animate.track} />
  <TweenOne component="g" animation={animate.greenBall} />
		

Анимировать с помощью этого метода довольно просто. Всё, что нужно, это описать правила анимации и перенести их в компонент TweenOne.

Заключение

Анимации для разных целей требуют разных подходов. Мы рассмотрели несколько решений, которые вы можете использовать в своих проектах. Выберите метод, который подходит вам.

 

Ниже вы найдёте бонус со списком популярных библиотек для анимации.

Библиотеки ReactJs

  1. react-motion — фреймворк, который решает проблемы с анимацией.
  2. react-spring — основанная на физике библиотека анимации React.
  3. ant-motion — спецификация Animate и компоненты Ant Design.
  4. react-move — красивые анимации, управляемые данными для React.
  5. react-flight — лучший способ создания анимационных композиций для React.
  6. react-flip-move — простая анимация между изменениями DOM (например, упорядочивание списка) с использованием техники FLIP.
  7. react-burger-menu — компонент боковой панели с коллекцией эффектов и стилей, использующих переходы CSS и анимацию пути SVG.
  8. animated — Библиотека Декларативных Анимаций для React и React Native.
  9. react-tween-state — анимация React.
  10. react-animations — коллекция анимаций для inline-style библиотек.

Библиотеки Javascript

  1. GSAP — ультра высокопроизводительная профессиональная анимация для современного Интернета.
  2. Anime.js — Anime.js (/ˈæn.ə.meɪ/) – это лёгкая библиотека анимации JavaScript с простым, но мощным API. Работает со свойствами CSS, SVG, атрибутами DOM и объектами JavaScript.
  3. Popmotion — простые библиотеки анимации для восхитительных пользовательских интерфейсов.
  4. vivus — библиотека JavaScript для рисования анимации в SVG.
  5. svg.js — лёгкая библиотека для управления и анимации SVG.
  6. velocity — это анимационный движок с тем же API, что и у jQuery $.animate().
  7. wow — показывает анимации при прокрутке. Дружит с animate.css.
  8. dynamic.js — библиотека JavaScript для создания анимации на основе физики.
  9. granim.js — создавайте плавные и интерактивные градиентные анимации с помощью этой небольшой библиотеки Javascript.
  10. kute.js — это встроенный движок JavaScript-анимации с отличным качеством кода и хорошей производительностью.
  11. TweenJs — простая, но мощная библиотека межкадрового заполнения / анимации для Javascript. Часть набора библиотек CreateJS.
  12. moveTo — лёгкая JavaScript библиотека анимации прокрутки без зависимостей.

Используйте анимацию с умом!

Спасибо за внимание! Занимайтесь тем, что вам нравится, продолжайте учиться и не прекращайте кодить.

А если вам понравилась статья, подписывайтесь на автора на Medium и LinkedIn.