5 отличных способов анимировать React-приложения в 2019 году

Анимация в приложениях React — популярная и обсуждаемая тема. Дело в том, что способов ее создания очень много. Некоторые разработчики используют CSS, добавляя теги в HTML-классы. Отличный способ, его стоит применять. Но, если вы хотите работать со сложными видами анимаций, стоит уделить время изучению GreenSock, это популярная и мощная платформа. Также для создания анимаций существует масса библиотек и компонентов. Давайте поговорим о них.

В статье рассматривается пять способов анимирования React-приложений:

  • CSS;
  • ReactTransitionGroup;
  • React-animations;
  • React-reveal;
  • TweenOne и Ant Design.

Skillbox рекомендует: Образовательный онлайн-курс «Профессия Java-разработчик».
Напоминаем: для всех читателей «Хабра» — скидка 10 000 рублей при записи на любой курс Skillbox по промокоду «Хабр».

Все примеры доступны в репозитории (отсюда в статью вставлены исходники вместо картинок, как в оригинальной статье).

CSS

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

В качестве примера — анимированное меню:

Оно относительно простое, со свойством CSS и триггером типа className = «is-nav-open» для тега HTML.

Использовать этот метод можно разными способами. Например, создать над навигацией wrapper, а затем вызывать изменения полей. Поскольку навигация имеет постоянную ширину, которая равна 250px, ширина wrapper со свойством margin-left или translateX должна иметь ту же ширину. При необходимости показать навигацию нужно добавить className = «is-nav-open» для wrapper и переместить wrapper на margin-left / translateX: 0;.

В конечном итоге исходник анимации будет выглядеть следующим образом:

export default class ExampleCss extends Component {     handleClick() {         const wrapper = document.getElementById('wrapper');         wrapper.classList.toggle('is-nav-open')     }     render() {         return (             <div id="wrapper" 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: 20px;     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; }

Повторюсь, если анимация относительно проста, то этот метод — основной. Пользователей порадует быстродействие браузера.

ReactTransitionGroup

Компонент ReactTransitionGroup разработала команда сообщества ReactJS. С его помощью можно без проблем реализовать основные CSS-анимации и переходы.

ReactTransitionGroup предназначен для изменения классов при изменении жизненного цикла компонента. У него небольшой размер, его нужно установить в пакете для React-приложения, что незначительно увеличит общий размер сборки. Кроме того, можно использовать и CDN.

У ReactTransitionGroup есть три элемента, это Transition, CSSTransition и TransitionGroup. Для запуска анимации в них нужно обернуть компонент. Стиль, в свою очередь, нужно прописывать в классах CSS.

Вот анимация, а дальше — способ ее реализации.

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

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

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

.example-eneter {     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.

Для удаления контакта по индексу в массиве state.items используется handleRemove.

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 });     }       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>     ); } };

React-animations

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

Я знаю, что вы думаете:

Теперь проверим, как это работает на примере анимации подпрыгивания.

Первым делом импортируем анимацию из 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.

React-reveal

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

import Fade from 'react-reveal/Fade';   <Fade top>     <h1>Title</h1> </Fade>

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

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. В массив включены пять элементов. После использования метода массива map есть возможность рендерить любой элемент в компонентах Fade, вставляя элементы в заголовок. Стили, которые определены в константе styles, получают короткие CSS-стили как для блока, так и для заголовка. Выше — пять блоков с анимацией Fade.

TweenOne и анимация в Ant Design

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

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

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

TweenOne.plugins.push(PathPlugin);

Основными параметрами анимации являются следующие:

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

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

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, };

Теперь можно приступать к созданию объекта анимации.

  • redSquare содержит параметры цикла плюс координату Y, длительность и задержку.
  • greenBall содержит путь с параметрами объекта x, у — значение 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. TweenOne — базовый компонент с базовыми же proprs и анимационными 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>     );   }

Да, выглядит страшновато, но анимация с использованием этого метода проста.

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

Требуется всего лишь описать правила анимации и перенести их в компонент TweenOne.

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

Skillbox рекомендует:

FavoriteLoadingДобавить в избранное
Posted in Без рубрики

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *