小能豆

React-动画安装和卸载单个组件

javascript

这么简单的事情应该很容易完成,但我却为它的复杂程度而苦恼。

我只想为 React 组件的安装和卸载制作动画,仅此而已。以下是我到目前为止尝试过的方法以及每个解决方案不起作用的原因:

  1. ReactCSSTransitionGroup- 我根本没有使用 CSS 类,都是 JS 样式,所以这不起作用。
  2. ReactTransitionGroup- 这个低级 API 很棒,但它要求您在动画完成时使用回调,因此仅使用 CSS 过渡在这里不起作用。总是有动画库,这引出了下一点:
  3. GreenSock——在我看来,该许可对于商业用途来说太过严格。
  4. React Motion-这看起来很棒,但是TransitionMotion对于我的需求来说极其令人困惑和过于复杂。
  5. 当然,我可以像 Material UI 那样使用一些小技巧,让元素被渲染但保持隐藏状态 ( left: -10000px),但我宁愿不走这条路。我认为这很不靠谱,我希望卸载我的组件,这样它们就会变得干净,不会弄乱 DOM。

我想要一个易于实现的东西。安装时,为一组样式设置动画;卸载时,为相同(或另一组)样式设置动画。完成。它还必须在多个平台上具有高性能。

我在这里遇到了难题。如果我遗漏了什么,并且有简单的方法可以解决此问题,请告诉我。


阅读 44

收藏
2024-06-05

共1个答案

小能豆

这有点冗长,但我已经使用了所有本机事件和方法来实现此动画。没有ReactCSSTransitionGroupReactTransitionGroup等等。

我使用过的东西

  • React 生命周期方法
  • onTransitionEnd事件

工作原理

  • 根据传递的挂载属性(mounted)和默认样式(opacity: 0)挂载元素
  • 挂载或更新后,使用componentDidMountcomponentWillReceiveProps进行进一步更新)更改样式(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>
2024-06-05