
L'animation dans les applications React est un sujet populaire et discuté. Le fait est qu'il existe de nombreuses façons de le créer. Certains développeurs utilisent CSS en ajoutant des balises aux classes HTML. Excellent moyen, cela vaut la peine d'être utilisé. Mais, si vous voulez travailler avec des types d'animations complexes, vous devriez prendre le temps d'étudier GreenSock, c'est une plateforme populaire et puissante. Il existe également de nombreuses bibliothèques et composants pour créer des animations. Parlons-en.
Cet article décrit cinq façons d'animer des applications React:
- CSS
- ReactTransitionGroup;
- React-animations
- Réagir-révéler;
- TweenOne et Ant Design.
Skillbox recommande: Le cours éducatif en ligne "Profession Java-developer" .
Nous vous rappelons: pour tous les lecteurs de «Habr» - une remise de 10 000 roubles lors de l'inscription à un cours Skillbox en utilisant le code promo «Habr».
Tous les exemples sont disponibles dans le
référentiel (à partir d'ici, des sources sont insérées dans l'article au lieu d'images, comme dans l'article d'origine).
CSS
Cette méthode a été mentionnée au tout début et elle est vraiment bonne. Si au lieu d'importer des bibliothèques JavaScript et de l'utiliser, l'assemblage sera petit, le navigateur n'aura pas besoin de beaucoup de ressources. Et cela, bien sûr, affecte les performances des applications. Si votre animation doit être relativement simple, faites attention à cette méthode.
Un exemple est un menu animé:

C'est relativement simple, avec une propriété CSS et un déclencheur comme className = "is-nav-open" pour la balise HTML.
Il existe plusieurs façons d'utiliser cette méthode. Par exemple, créez un wrapper sur la navigation, puis appelez les modifications de champ. Étant donné que la navigation a une largeur constante de 250 pixels, la largeur de l'encapsuleur avec la propriété margin-left ou translateX doit être la même largeur. Si vous devez afficher la navigation, ajoutez className = "is-nav-open" pour le wrapper et déplacez le wrapper vers margin-left / translateX: 0;.
Au final, la source d'animation ressemblera à ceci:
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> ); } }
Et voici les styles 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; }
Je le répète, si l'animation est relativement simple, alors cette méthode est la principale. Les utilisateurs apprécieront les performances du navigateur.
ReactTransitionGroup
Le composant ReactTransitionGroup a été développé par l'équipe de la communauté ReactJS. Avec lui, vous pouvez facilement implémenter des animations et des transitions CSS de base.
ReactTransitionGroup est conçu pour changer de classe lors de la modification du cycle de vie des composants. Il a une petite taille, il doit être installé dans le package de l'application React, ce qui augmentera légèrement la taille globale de l'assemblage. De plus, vous pouvez utiliser CDN.
ReactTransitionGroup comprend trois éléments, à savoir Transition, CSSTransition et TransitionGroup. Pour démarrer l'animation, vous devez envelopper le composant en eux. Le style, à son tour, doit être écrit dans les classes CSS.
Voici l'animation, puis la façon de l'implémenter.

La première étape consiste à importer le CSSTransitionGroup à partir de react-transition-group. Après cela, vous devez encapsuler la liste et définir la propriété transitionName. Chaque fois que vous ajoutez ou supprimez un enfant dans un CSSTransitionGroup, il obtient des styles animés.
<CSSTransitionGroup transitionName="example"> {items} </CSSTransitionGroup>
Lorsque vous définissez la propriété transitionName = "example", les classes dans les feuilles de style doivent commencer par le nom de l'exemple.
.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;
Ci-dessus est un exemple utilisant ReactTransitionGroup.
Vous avez également besoin de logique et de deux méthodes pour implémenter l'exemple d'ajout d'une liste de contacts.
La première méthode handleAdd - elle ajoute de nouveaux contacts, obtient un nom aléatoire, qu'il place ensuite dans le tableau state.items.
Pour supprimer un contact par index dans le tableau state.items, utilisez 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> ); } };
Réagissez aux animations
React-animations est une bibliothèque basée sur animate.css. C'est facile à travailler, elle a de nombreuses collections d'animations différentes. La bibliothèque est compatible avec toute bibliothèque de style en ligne qui prend en charge l'utilisation d'objets pour définir les images principales d'une animation, y compris Radium, Aphrodite ou les composants de style.

Je sais ce que tu en penses:

Voyons maintenant comment cela fonctionne avec l'exemple d'une animation de rebond.

Tout d'abord, importez l'animation à partir de react-animations.
const Bounce = styled.div`animation: 2s $ {keyframes` $ {bounce} `} infinite`;Ensuite, après avoir créé le composant, nous encapsulons tout code HTML ou composant pour l'animation.
<bounce><h1>Hello Animation Bounce</h1></bounce>
Un exemple:
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> ); } }
Tout fonctionne, l'animation est très simple. De plus, il existe une excellente solution pour utiliser l'animation de rebond lors du défilement -
react-animate-on-scroll .
Réagir-révéler
Le framework React Reveal propose des animations de base, notamment la décoloration, la réflexion, la mise à l'échelle, la rotation, etc. Il permet de travailler avec toutes les animations à l'aide d'accessoires. Ainsi, vous pouvez spécifier des paramètres supplémentaires, y compris la position, le retard, la distance, la cascade et autres. D'autres effets CSS peuvent être utilisés, notamment le rendu côté serveur et les composants de haut niveau. En général, si vous avez besoin d'une animation de défilement, vous devez utiliser ce framework.
import Fade from 'react-reveal/Fade'; <Fade top> <h1>Title</h1> </Fade>

Il y a cinq blocs au total, chacun d'eux a une page plein écran et un titre.
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, }, };
Nous présentons maintenant la constante animateList. Le tableau comprend cinq éléments. Après avoir utilisé la méthode de tableau de carte, il est possible de rendre n'importe quel élément dans les composants Fade en insérant des éléments dans l'en-tête. Les styles définis dans la constante styles obtiennent des styles CSS courts pour le bloc et l'en-tête. Au-dessus se trouvent cinq blocs avec animation Fade.
TweenOne et animation dans Ant Design
Ant Design est une bibliothèque React UI qui contient un grand nombre de composants utiles et faciles à utiliser. Il convient si vous devez créer des interfaces utilisateur élégantes. Développé par Alibaba, qui utilise la bibliothèque dans de nombreux projets.

L'exemple a pas mal de composants animés. La plupart d'entre eux ont des animations similaires, donc un exemple d'implémentation sera plus simple que ci-dessus. Cela comprendra uniquement une balle, une balle verte et un élément supplémentaire, par exemple, un carré rouge.

L'animation utilise le composant TweenOne, qui a besoin de PathPlugin pour définir correctement la trajectoire. Tout cela ne fonctionnera que s'il est placé
PathPlugin sur TweenOne.plugins.
TweenOne.plugins.push(PathPlugin);
Les principales options d'animation sont les suivantes:
- durée - temps d'animation en ms;
- facilité - fluidité de l'animation;
- yoyo - changement de mouvement en avant et en arrière à chaque répétition;
- répéter - répéter l'animation. Vous devez utiliser -1 pour une animation sans fin;
- p - les coordonnées du chemin pour l'animation;
- easyPath - coordonnées du chemin lisse pour l'animation.
Les deux derniers paramètres sont très spécifiques, mais ne vous en faites pas, tout fonctionne comme il se doit.
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, };
Vous pouvez maintenant commencer à créer l'objet d'animation.
- redSquare contient les paramètres de boucle ainsi que la coordonnée Y, la durée et le retard.
- greenBall contient le chemin avec les paramètres de l'objet x, y - la valeur de p. De plus, la durée, la répétition et la fluidité sont fonction de TweenOne.easing.path, qui a deux paramètres.
- chemin - easepath.
- lengthPixel est une courbe divisée en seulement 400 sections.
- piste - un ovale avec des axes, il a des styles de cycle et un paramètre de rotation.
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, }, };
Vous devez également faire attention au composant TweenOne. Tous les composants seront importés de rc-tween-one. TweenOne est un composant de base avec des proprs de base et des props animés, qui sont des animations. Chaque TweenOne a ses propres paramètres d'animation, tels que 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> ); }
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 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> ); }

Oui, cela semble effrayant, mais l'animation utilisant cette méthode est simple.
<TweenOne component="g" animation={animate.redSquare} /> <TweenOne component="g" animation={animate.track} /> <TweenOne component="g" animation={animate.greenBall} />
Il vous suffit de décrire les règles d'animation et de les transférer vers le composant TweenOne.
Pour atteindre différents objectifs, différentes approches sont nécessaires. Cet article a examiné plusieurs solutions pouvant être utilisées dans un grand nombre de projets. Votre entreprise consiste à choisir la bonne.
Skillbox recommande: