2015-10-29 67 views
11

我想在两个组件之间创建动画,其中第一个组件淡出并在将下一个组件添加到DOM并淡入之前从DOM中删除。否则,将新组件添加到DOM并在旧组件移除之前占用空间。你可以看到在这个小提琴问题:React组件之间的动画过渡

http://jsfiddle.net/phepyezx/4

// css snippet 
.switch-enter { 
    opacity: 0.01; 
} 
.switch-enter.switch-enter-active { 
    opacity: 1.0; 
} 
.switch-leave { 
    opacity: 1.0; 
} 
.switch-leave.switch-leave-active { 
    opacity: 0; 
} 

// React snippet 
<ReactCSSTransitionGroup transitionName="switch"> 
    <div key={key} className={className}>{this.text()}</div> 
</ReactCSSTransitionGroup> 

一段不被接受的解决方案(对我来说)是过渡到新的组件之前隐藏的CSS原始的组件,如下所示:

http://jsfiddle.net/phepyezx/5

// Change to css 
.switch-leave { 
    visibility: hidden; 
    height: 0px; 
    width: 0px; 
    opacity: 1.0; 
} 

是否有“延迟”从之前的原始安装一个新的成分反应被移除的路?我愿意为速度或其他图书馆来实现这一点。

由于

回答

8

使用componentWillUnmount()生命周期方法求解。

http://jsfiddle.net/phepyezx/9/

下面的代码:

var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup; 

const Off = React.createClass({ 
    componentWillUnmount() { 
     this.props.handleTransitionEnd(); 
    }, 
    render() { 
     return (
      <div className="off button">OFF</div> 
     ) 
    } 
}); 

const On = React.createClass({ 
    componentWillUnmount() { 
     this.props.handleTransitionEnd(); 
    }, 
    render() { 
     return (
      <div className="on button">ON</div> 
     ) 
    } 
}); 

var Switch = React.createClass({ 
    getInitialState: function() { 
     return { 
      on: false, 
      transitionEnd: true 
     }; 
    }, 

    toggle: function(e) { 
     this.setState({ 
      on: !this.state.on, 
      transitionEnd: false 
     }); 
    }, 

    handleTransitionEnd() { 
     this.setState({transitionEnd: true}); 
    }, 

    renderOff() { 
     if (! this.state.on && this.state.transitionEnd) { 
      return (
       <Off key="off" handleTransitionEnd={this.handleTransitionEnd} /> 
      ) 
     } 
    }, 

    renderOn() { 
     if (this.state.on && this.state.transitionEnd) { 
      return (
       <On key="on" handleTransitionEnd={this.handleTransitionEnd} /> 
      ) 
     } 
    }, 

    render: function() { 
     return (
      <div> 
       <button onClick={this.toggle}>Toggle</button> 
       <ReactCSSTransitionGroup transitionName="switch"> 
       {this.renderOff()} 
       {this.renderOn()} 
       </ReactCSSTransitionGroup> 
      </div> 
     );   
    } 
}); 

React.render(<Switch/>, document.getElementById("switch")); 

和相关的CSS:

.switch-enter { 
    opacity: 0.01; 
} 
.switch-enter.switch-enter-active { 
    opacity: 1.0; 
    transition: opacity 500ms ease-in; 
} 
.switch-leave { 
    opacity: 1.0; 
} 
.switch-leave.switch-leave-active { 
    opacity: 0; 
    transition: opacity 500ms ease-out; 
} 

您可以Jonny Buchanan's answer它使用绝对定位,取而代之的延迟达到同样的效果显componentWillUnmount()

+0

@JonnyBuchanan你的两种方法都很有用。现在已经过去了一段时间,您发现哪种方法最适合您? @RickJolly,你的方法在某些场景下效果最好吗?它是否扩展到ReactCSSTransitionGroup中的更大的列表(子)?我理解的方式是你的方法会产生一个额外的'render()'循环(可能每个项目被删除的额外周期?)。你有没有发现有优点或缺点? (也许是调试 - 也许它更清晰地说明了额外的render()是怎么回事?) –

+0

另外,要动画的组件必须实现'componentWillUnmount',以及'handleTransitionEnd()'作为'道具'...你有没有发现这是对其他情况的痛苦? –

13

另一种解决方案是使进入和离开元件占用相同的空间中,例如通过使它们都绝对定位:

<ReactCSSTransitionGroup 
    className="container" 
    component="div" 
    transitionName="switch"> 
... 

.container { 
    position: relative; 
} 
.container > div { 
    position: absolute; 
} 

http://jsfiddle.net/phepyezx/7/


您可以使用transition-delay等到离开组件消失后再使输入组件出现,例如:

.fade-enter { 
    opacity: 0.01; 
} 
.fade-enter.fade-enter-active { 
    opacity: 1; 
    transition: opacity 1s; 
    transition-delay: 1s; 
} 

.fade-leave { 
    opacity: 1; 
} 
.fade-leave.fade-leave-active { 
    opacity: 0.01; 
    transition: opacity 1s; 
} 
+0

是。虽然效果不如完全淡出原来的效果。我猜我以后不可能没有欺骗。我想我可以使用较低级别的ReactTransisionGroup的componentDidLeave()生命周期方法来设置状态,以便知道何时开始呈现新组件。另一种方法是使用普通javascript在底层DOM节点上应用一个transitionend事件侦听器,如本文所示:http://www.chloechen.io/react-animation-done-in-twoways/ –

+0

增加了一个新的有关'过渡延迟' –

+0

非常感谢!几乎完美,但需要绝对定位。不幸的是,在传出组件离开之前,我还没有发现任何传入组件未安装(或显示:'none')的地方。 –

2

如果你想推迟下一个组件的渲染,你可以使用这样的事情:

import React, { Component } from 'react'; 

export default class DelayedRender extends Component { 

    static propTypes = { 
     delay: React.PropTypes.number.isRequired, 
     children: React.PropTypes.element, 
     className: React.PropTypes.string 
    }; 

    constructor(props) { 
     super(props); 

     this.state = { 
      render: false 
     }; 
    } 

    componentDidMount() { 
     setTimeout(() => { 
      const delayedClassNames = this.refs.noDelayed.className; 
      this.setState({ 
       render: true, 
       classNames: delayedClassNames 
      }); 
     }, this.props.delay); 
    } 

    render() { 
     const { children, className } = this.props; 
     return this.state.render ? 
      <div className={this.state.classNames}>{children}</div> : 
      <div className={className} ref="noDelayed" ></div>; 
    } 
} 

而在你的渲染方法:

const ROUTE_TRANSITION_TIME = 500; 
const views = []; 

if (shouldRenderDelayedRoute) { 
    views.push(
     <DelayedRender delay={ROUTE_TRANSITION_TIME} key="book"> 
      <A ref="book"/> 
     </DelayedRender> 
    ); 
} else { 
    views.push(<B key="library"/>); 
} 

<ReactCSSTransitionGroup 
    transitionEnterTimeout={ROUTE_TRANSITION_TIME} 
    transitionLeaveTimeout={ROUTE_TRANSITION_TIME} 
    transitionName="fade-transition" 
         > 
    {views} 
</ReactCSSTransitionGroup>