【问题标题】:React smooth transition between different states in a component反应组件中不同状态之间的平滑过渡
【发布时间】:2017-04-23 04:38:19
【问题描述】:

我有一个像这样的简单组件:

var component = React.createClass({
  render: function(){
      if (this.props.isCollapsed){
         return this.renderCollapsed();
      }
      return this.renderActive()
  },
  renderActive: function(){
    return (
      <div>
      ...
      </div>
    );
  },
  renderCollapsed: function(){
    return (
      <div>
      ...
      </div>
    );
  },
});

基本上,当属性发生变化时,组件要么显示活动状态,要么显示折叠状态。

我在想的是,当属性更改发生时,即 active->collapse 或相反,我希望旧视图平滑地“缩小”或“展开”以显示新视图。例如,如果它是活动的->折叠的,我希望活动的UI缩小到折叠UI的大小,并平滑显示。

我不知道如何实现这个效果。请分享一些想法。谢谢!

【问题讨论】:

    标签: javascript css reactjs css-transitions


    【解决方案1】:

    这是一个最小的工作示例:

    const collapsible = ({active, toggle}) =>
    <div>
      <button type="button" onClick={toggle}>Toggle</button>
      <div className={'collapsible' + (active? ' active': '')}>
        text
      </div>
    </div>
    
    
    const component = React.createClass({
      getInitialState() {
        return {active: false}
      },
      toggle() {
        this.setState({active: !this.state.active})
      },
      render() {
        return collapsible({active: this.state.active, toggle: this.toggle})
      }
    })
    ReactDOM.render(React.createElement(component), document.querySelector('#root'))
    .collapsible {
      height: 1.5rem;
      transition: height 0.25s linear;
      background: #333;
      border-radius: 0.25rem
    }
    .collapsible.active {
      height: 7rem
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>
    <div id="root"></div>

    视图可以通过CSS transition平滑地“缩小”或“展开”,通过改变CSS属性触发。

    要使用 React 控制 CSS 属性,我们可以将状态更改反映到属性值或 className 中的 render()

    在本例中,.active 类影响height 值,并由state.active 控制。该类由 React 切换以响应状态变化,并触发 CSS 转换。

    如需更平滑的过渡,请参阅this article

    【讨论】:

      【解决方案2】:

      而不是有条件地渲染 a 的两个不同的结束状态 组件,您可以改为
      切换类 零件。您可以有如下活动和折叠的类:

      例如:

      .active{
        -webkit-transition: -webkit-transform .5s linear;  // transition of 
                                                           // 0.5 of a second
        height: 200px;
      }
      
      .collapsed{
        height: 0px;
      }
      

      查看this resource 的示例

      【讨论】:

        【解决方案3】:

        标准方法是使用react-transition-group 中的CSSTransitionGroup,这很简单。用CSSTransitionGroup 包裹组件,并在进入和离开时设置超时,如下所示:

        <CSSTransitionGroup
              transitionName="example"
              transitionEnterTimeout={500}
              transitionLeaveTimeout={300}>
              {items}
        </CSSTransitionGroup>
        

        来自v1-stable docs

        "在这个组件中,当一个新项目被添加到 CSSTransitionGroup 它 将获得 example-enter CSS 类和 example-enter-active CSS 在下一个刻度中添加的类。”

        为 CSS 类添加样式以获得正确的动画。

        React docs也有很好的解释,看看吧。

        动画也有第三方组件。

        【讨论】:

          【解决方案4】:

          解决这种情况的另一种方法可能是在动画完成后更改状态。它的好处是你不仅可以应用过渡,还可以应用任何你想要的动作(js动画、smil等),主要是不要忘记调用结束回调;)

          这是工作示例CodePen

          下面是代码示例:

          const runTransition = (node, {property = 'opacity', from, to, duration = 600, post = ''}, end) => {
            const dif = to - from;
            const start = Date.now();
          
            const animate = ()=>{
              const step = Date.now() - start;
              if (step >= duration) {
                node.style[property] = to + post;
                return typeof end == 'function' && end();
              }
          
              const val =from + (dif * (step/duration));
              node.style[property] =  val + post;
              requestAnimationFrame(animate);
            }
          
            requestAnimationFrame(animate);
          
          }
          
          class Comp extends React.Component {
            constructor(props) {
              super(props);
              this.state = {
                isCollapsed: false
              }
          
              this.onclick = (e)=>{
                 this.hide(e.currentTarget,()=>{
                   this.setState({isCollapsed: !this.state.isCollapsed})
                 });
               };
          
              this.refF = (n)=>{
                 n && this.show(n);
               };
            }
          
            render() {
              if (this.state.isCollapsed){
                   return this.renderCollapsed();
                }
                return this.renderActive()
            }
          
            renderCollapsed() {
              return (
                <div 
                  key='b'
                  style={{opacity: 0}}
                  ref={this.refF}
                  className={`b`}
                  onClick={this.onclick}>
                    <h2>I'm Collapsed</h2>
                </div>
                )
            }
          
            renderActive() {
              return (
                <div 
                  key='a'
                  style={{opacity: 0}}
                  ref={this.refF}
                  className={`a`}
                  onClick={this.onclick}>
                    <h2>I'm Active</h2>
                </div>
                )
            }
          
            show(node, cb)  {
              runTransition(node, {from: 0, to: 1}, cb);
            }
          
            hide(node, cb) {
              runTransition(node, {from: 1, to: 0}, cb);
            }
          
          }
          
          ReactDOM.render(<Comp />, document.getElementById('content'));
          

          当然,要让这种方法发挥作用,你唯一的机会是中继状态,而不是 Component 的 props,如果你必须处理它们,你总是可以在 componentWillReceiveProps 方法中设置它们。

          更新

          Codepen link 更新了更清晰的示例,展示了这种方法的好处。转场改为javascript动画,不依赖transitionend事件。

          【讨论】:

            【解决方案5】:

            由于您想为每个活动组件和折叠组件呈现两个不同的组件,请将它们包装在一个 div 中,借助 CSS 控制高度。

            render: function(){
            var cls = this.props.isCollapsed() ? 'collapsed' : 'expanded';
            return(
              <div className={cls + ' wrapper'}>
                {
                  this.props.isCollapsed() ?
                   this.renderCollapsed() :
                   this.renderActive()
                }
              </div>
            );
            }
            

            在你的 CSS 中:

            .wrapper{
              transition: transform .5s linear;
            }
            
            .expanded{  
              height: 200px;
            }
            
            .collapsed{
              height: 20px;
            }
            

            【讨论】:

              【解决方案6】:

              您可以添加一个描述活动状态的类,即。 .active 并在切换状态时切换该类。

              css 应该如下所示:

              .your-component-name{
                // inactive css styling here
              }
              
              .your-component-name.active {
                // active css styling here
              }
              

              【讨论】:

                【解决方案7】:

                这里你有一个使用 Velocity-React 库的 Toggle react 组件,它非常适合在 React uis 中为过渡提供动画:

                import React, { Component } from 'react';
                import { VelocityTransitionGroup } from 'velocity-react';
                
                export default class ToggleContainer extends Component {
                    constructor () {
                        super();
                
                        this.renderContent = this.renderContent.bind(this);
                    }
                
                    renderContent () {
                        if (this.props.show) {
                            return (
                                <div className="toggle-container-container">
                                    {this.props.children}
                                </div>
                            );
                        }
                        return null
                    }
                
                    render () {
                        return (
                            <div>
                                <h2 className="toggle-container-title" onClick={this.props.toggle}>{this.props.title}</h2>
                                <VelocityTransitionGroup component="div" enter="slideDown" leave="slideUp">
                                    {this.renderContent()}
                                </VelocityTransitionGroup>
                            </div>
                        );
                    }
                
                };
                
                ToggleContainer.propTypes = {
                    show: React.PropTypes.bool,
                    title: React.PropTypes.string.isRequired,
                    toggle: React.PropTypes.func.isRequired,
                };
                

                希望对你有帮助!

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2011-09-21
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2016-01-30
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多