146 votes

React - animer le montage et le démontage d'un seul composant

Quelque chose d'aussi simple devrait être facile, mais je suis en tirant mes cheveux de plus combien il est compliqué.

Tout ce que je veux faire est d'animer le montage et le démontage de Réagir composant, c'est tout. Voici ce que j'ai essayé jusqu'à présent et pourquoi chaque solution ne fonctionne pas:

  1. ReactCSSTransitionGroup - Je ne suis pas en utilisant des classes CSS à tous, c'est JS styles, de sorte que cela ne fonctionnera pas.
  2. ReactTransitionGroup - Cette baisse du niveau de l'API est grande, mais il vous oblige à utiliser une fonction de rappel lorsque l'animation est terminée, donc juste en utilisant les transitions CSS ne fonctionne pas ici. Il y a toujours de l'animation de bibliothèques, ce qui nous amène au point suivant:
  3. GreenSock - La licence est trop restrictif pour une utilisation professionnelle de l'OMI.
  4. Réagir de Mouvement - Ce qui semble excellent, mais TransitionMotion est extrêmement confus et trop compliqué pour ce que j'en ai besoin.
  5. Bien sûr que je peux le faire ruse comme Matériel de l'INTERFACE utilisateur, où les éléments sont rendus, mais qui restent cachés (left: -10000px) mais je préfère ne pas aller dans cette voie. Je considère qu'il hacky, et je veux mes composants à démonter, donc ils nettoient et ne sont pas encombrer les DOM.

Je veux quelque chose qui est facile à mettre en œuvre. Sur le mont, l'animation d'un ensemble de styles; sur le démonter, animer le même (ou un autre), jeu de styles. Fait. Il a également pour assurer une haute performance sur plusieurs plates-formes.

J'ai frappé un mur de briques ici. Si je suis absent quelque chose et il y a un moyen facile de le faire, faites le moi savoir.

128voto

Pranesh Ravi Points 9005

C'est un peu long mais j'ai utilisé tous les natifs des événements et des méthodes pour réaliser cette animation. Pas de ReactCSSTransitionGroup, ReactTransitionGroup et etc.

Les choses que j'ai utilisé

  • Réagir cycle de vie des méthodes
  • onTransitionEnd événement

Comment cela fonctionne

  • Monter l'élément de base sur le mont prop passé(mounted) et avec le style par défaut(opacity: 0)
  • Après montage ou de mise à jour, utilisez componentDidMount (componentWillReceiveProps pour plus de mises à jour)pour modifier le style (opacity: 1) avec un délai d'attente(pour faire async).
  • Au cours de démonter, de passer d'une béquille pour le composant afin d'identifier les démonter, changer le style nouveau(opacity: 0), onTransitionEnd, suppression de démonter l'élément du DOM.

Continuer le cycle.

Passer par le code, vous allez comprendre. Si des éclaircissements sont nécessaires, veuillez laisser un commentaire.

Espérons que cette aide.

class App extends React.Component{
  constructor(props) {
    super(props)
    this.transitionEnd = this.transitionEnd.bind(this)
    this.mountStyle = this.mountStyle.bind(this)
    this.unMountStyle = this.unMountStyle.bind(this)
    this.state ={ //base css
      show: true,
      style :{
        fontSize: 60,
        opacity: 0,
        transition: 'all 2s ease',
      }
    }
  }
  
  componentWillReceiveProps(newProps) { // check for the mounted props
    if(!newProps.mounted)
      return this.unMountStyle() // call outro animation when mounted prop is false
    this.setState({ // remount the node when the mounted prop is true
      show: true
    })
    setTimeout(this.mountStyle, 10) // call the into animation
  }
  
  unMountStyle() { // css for unmount animation
    this.setState({
      style: {
        fontSize: 60,
        opacity: 0,
        transition: 'all 1s ease',
      }
    })
  }
  
  mountStyle() { // css for mount animation
    this.setState({
      style: {
        fontSize: 60,
        opacity: 1,
        transition: 'all 1s ease',
      }
    })
  }
  
  componentDidMount(){
    setTimeout(this.mountStyle, 10) // call the into animation
  }
  
  transitionEnd(){
    if(!this.props.mounted){ // remove the node on transition end when the mounted prop is false
      this.setState({
        show: false
      })
    }
  }
  
  render() {
    return this.state.show && <h1 style={this.state.style} onTransitionEnd={this.transitionEnd}>Hello</h1> 
  }
}

class Parent extends React.Component{
  constructor(props){
    super(props)
    this.buttonClick = this.buttonClick.bind(this)
    this.state = {
      showChild: true,
    }
  }
  buttonClick(){
    this.setState({
      showChild: !this.state.showChild
    })
  }
  render(){
    return <div>
        <App onTransitionEnd={this.transitionEnd} mounted={this.state.showChild}/>
        <button onClick={this.buttonClick}>{this.state.showChild ? 'Unmount': 'Mount'}</button>
      </div>
  }
}

ReactDOM.render(<Parent />, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react-with-addons.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

17voto

Mingrui Zhang Points 51

J'ai répliqué ce problème au cours de mon travail, et simple, comme il semble, il n'est vraiment pas à Réagir. Dans un scénario où vous afficher quelque chose comme:

this.state.show ? {childen} : null;

en tant que this.state.show changements les enfants sont montés/démontés.

Une approche que j'ai utilisée est la création d'un composant wrapper Animate et de l'utiliser comme

<Animate show={this.state.show}>
  {childen}
</Animate>

maintenant que this.state.show changements, nous pouvons percevoir prop changements avec getDerivedStateFromProps(componentWillReceiveProps) et de créer rendu intermédiaires étapes pour effectuer des animations.

A stage cycle might look like this

Nous commençons avec Statique de la Scène lorsque l'enfant est monté ou démonté.

Une fois que nous détectons l' show indicateur change, nous entrons dans Prep Stade où nous en calculer les propriétés nécessaires comme height et width de ReactDOM.findDOMNode.getBoundingClientRect().

En entrant Animer État , nous pouvons utiliser les css transition pour modifier la hauteur, la largeur et l'opacité de 0 à la valeur calculée (ou à 0 si le démontage).

À la fin de la transition, nous utilisons onTransitionEnd api pour revenir à Static stade.

Il y a beaucoup plus de détails à la façon dont les étapes de transfert en douceur, mais cela pourrait être une idée générale:)

Si quelqu'un intéressé, j'ai créé une bibliothèque Réagir https://github.com/MingruiZhang/react-animate-mount pour partager ma solution. Tous les commentaires sont les bienvenus:)

16voto

ffxsam Points 6487

En utilisant les connaissances acquises grâce à la réponse de Pranesh, j'ai proposé une autre solution configurable et réutilisable:

 const AnimatedMount = ({ unmountedStyle, mountedStyle }) => {
  return (Wrapped) => class extends Component {
    constructor(props) {
      super(props);
      this.state = {
        style: unmountedStyle,
      };
    }

    componentWillEnter(callback) {
      this.onTransitionEnd = callback;
      setTimeout(() => {
        this.setState({
          style: mountedStyle,
        });
      }, 20);
    }

    componentWillLeave(callback) {
      this.onTransitionEnd = callback;
      this.setState({
        style: unmountedStyle,
      });
    }

    render() {
      return <div
        style={this.state.style}
        onTransitionEnd={this.onTransitionEnd}
      >
        <Wrapped { ...this.props } />
      </div>
    }
  }
};
 

Usage:

 import React, { PureComponent } from 'react';

class Thing extends PureComponent {
  render() {
    return <div>
      Test!
    </div>
  }
}

export default AnimatedMount({
  unmountedStyle: {
    opacity: 0,
    transform: 'translate3d(-100px, 0, 0)',
    transition: 'opacity 250ms ease-out, transform 250ms ease-out',
  },
  mountedStyle: {
    opacity: 1,
    transform: 'translate3d(0, 0, 0)',
    transition: 'opacity 1.5s ease-out, transform 1.5s ease-out',
  },
})(Thing);
 

Et enfin, dans la méthode render un autre composant:

 return <div>
  <ReactTransitionGroup>
    <Thing />
  </ReactTransitionGroup>
</div>
 

2voto

Björn Holdt Points 266

Pour ceux qui envisagent de réaction de mouvement, l'animation d'un seul composant lorsqu'il monte et démonte peut être écrasante.

Il y a une bibliothèque appelée réagir-motion-ui-pack qui rend ce processus beaucoup plus facile à démarrer avec. C'est un wrapper autour de réaction de mouvement, ce qui signifie que vous obtenez tous les avantages de la bibliothèque (c'est à dire que vous êtes en mesure d'interrompre l'animation, de multiples démonte se produire en même temps).

Utilisation:

import Transition from 'react-motion-ui-pack'

<Transition
  enter={{ opacity: 1, translateX: 0 }}
  leave={{ opacity: 0, translateX: -100 }}
  component={false}
>
  { this.state.show &&
      <div key="hello">
        Hello
      </div>
  }
</Transition>

L'entrée définit ce que la fin de l'état de la composante; le congé est le style qui est appliquée lorsque le composant est démonté.

Vous pourriez trouver que une fois que vous avez utilisé le pack d'INTERFACE utilisateur une couple de fois, la réaction de la bibliothèque motion pourrait ne pas être aussi impressionnant plus.

1voto

Frazer Kirkman Points 492

Que se passe-t-il si onMount ajoute un autre nom de classe contenant la transition et supprime ce nom de classe?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X