1. ホーム
  2. animation

[解決済み] React - 単一のコンポーネントのマウントとアンマウントをアニメーション化する

2022-07-14 02:16:33

質問

こんな簡単なことが簡単にできるはずなのに、あまりの複雑さに身の毛がよだちます。

私がしたいことは、React コンポーネントのマウントとアンマウントをアニメーション化すること、それだけです。私がこれまでに試したことと、各ソリューションが動作しない理由は以下のとおりです。

  1. ReactCSSTransitionGroup - CSSのクラスは全く使っておらず、全てJSのスタイルなので、これではうまくいきません。
  2. ReactTransitionGroup - この低レベルのAPIは素晴らしいのですが、アニメーションが完了したときにコールバックを使用する必要があるので、CSSトランジションを使用するだけではここではうまくいきません。常にアニメーションライブラリがあり、それが次のポイントにつながります。
  3. GreenSock - ライセンスがビジネスユースには制限されすぎていると思います。
  4. React Motion - これは素晴らしいように見えますが TransitionMotion は非常にわかりにくく、私が必要とするものに対して過度に複雑です。
  5. もちろん、マテリアル UI のように、要素はレンダリングされるが非表示のままにしておくというトリックを行うこともできます ( left: -10000px ) を使用することもできますが、私はそのような方法を取りたくありません。私はそれをハチャメチャだと思っていますし、私は が欲しい コンポーネントをアンマウントして、DOM を散らかさないようにしたいのです。

私は何かが欲しいのです。 簡単 を実装したいです。マウント時に、スタイルのセットをアニメーション化し、アンマウント時に、同じ (または別の) スタイルのセットをアニメーション化します。完了です。また、複数のプラットフォームで高いパフォーマンスを発揮する必要があります。

私はここでレンガの壁にぶつかりました。もし私が何かを見落としていて、これを行う簡単な方法があるのなら教えてください。

どのように解決するのですか?

少し長くなりますが、このアニメーションを実現するために、すべてのネイティブイベントとメソッドを使用しました。いいえ ReactCSSTransitionGroup , ReactTransitionGroup など。

使ったことのあるもの

  • Reactのライフサイクルメソッド
  • onTransitionEnd イベント

この仕組み

  • 渡された mount prop に基づいて要素をマウントする( mounted ) と、デフォルトのスタイル ( opacity: 0 )
  • マウントまたはアップデート後に componentDidMount ( componentWillReceiveProps をクリックすると、スタイルが変更されます。 opacity: 1 ) をタイムアウトで変更します (非同期にするため)。
  • アンマウント時に、アンマウントを識別するためのpropをコンポーネントに渡し、再度スタイルを変更する( opacity: 0 ), onTransitionEnd で、DOMから要素をアンマウントする。

サイクルを継続します。

コードに目を通すと、理解できるはずです。何か説明が必要な場合は、コメントを残してください。

これが役立つことを願っています。

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>