简体   繁体   English

在组件中的不同状态之间进行平滑过渡

[英]React smooth transition between different states in a component

I have a simple component like this: 我有一个像这样的简单组件:

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>
    );
  },
});

Basically, when the property changes, the component will either show active state or collapse state. 基本上,当属性更改时,组件将显示活动状态或折叠状态。

What I am thinking is, when the property change happens, ie active->collapse, or the other way around, I want the old view "shrink" or "expand" smoothly to show the new view. 我在想的是,当财产发生变化时,即主动 - >崩溃,或者相反,我希望旧视图“缩小”或“展开”平滑以显示新视图。 For example, if it is active -> collapse, I want the active UI to shrink to the size of collapse UI, and show it smoothly. 例如,如果它处于活动状态 - >折叠,我希望活动UI缩小到折叠UI的大小,并平滑地显示它。

I am not sure how to achieve this effect. 我不知道如何达到这个效果。 Please share some ideas. 请分享一些想法。 Thanks! 谢谢!

Rather than conditionally render two different end states of a component, you could instead 您可以改为,而不是有条件地渲染组件的两个不同的最终状态
toggle the class on the same component. 在同一组件上切换类 You could have active and collapsed classes as follows: 您可以使用以下活动和折叠类:

For example: 例如:

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

.collapsed{
  height: 0px;
}

Check out this resource for examples 查看此资源以获取示例

Here is a minimal working example: 这是一个最小的工作示例:

 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> 

The view can be "shrink" or "expand" smoothly by CSS transition , which is triggered by changing CSS properties. CSS转换可以平滑地“缩小”或“展开”视图,这是通过更改CSS属性触发的。

To control CSS properties with React, we can reflect state changes to property values or className in render() . 要使用React控制CSS属性,我们可以在render()反映属性值或className状态更改。

In this example, .active class affects height value, and is controlled by state.active . 在此示例中, .active类影响height值,并由state.active控制。 The class is toggled by React in response to state changes, and triggers CSS transition. 响应状态更改,React切换该类,并触发CSS转换。

For smoother transitions, See this article . 要获得更平滑的过渡,请参阅此文章

The standard way is to use CSSTransitionGroup from react-transition-group , which is quite easy. 标准方法是使用react-transition-group中的 CSSTransitionGroup,这非常简单。 Wrap the component with the CSSTransitionGroup and set timeouts on enter and leave, like this: 使用CSSTransitionGroup包装组件并在进入和离开时设置超时,如下所示:

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

From the v1-stable docs : 来自v1-stable文档

"In this component, when a new item is added to CSSTransitionGroup it will get the example-enter CSS class and the example-enter-active CSS class added in the next tick." “在这个组件中,当一个新项目被添加到CSSTransitionGroup时,它将获得示例 - 输入CSS类,并在下一个tick中添加example-enter-active CSS类。”

Add styling for the CSS classes to get the correct animation. 为CSS类添加样式以获取正确的动画。

There's also pretty good explanation in React docs , check it out. React docs中也有很好的解释,请查看。

There are third-party components for animation as well. 还有动画的第三方组件。

One more approach to this situation might be changing state after animation completes. 这种情况的另一种方法可能是在动画完成后改变状态。 The benefits of it is that you can apply not only transitions but whatever actions you want (js animations, smil, etc ..), main thing is not to forget to call an end callback;) 它的好处是你不仅可以应用转换,而且可以应用你想要的任何动作(js动画,smil等等),主要的是不要忘记调用结束回调;)

Here is working example CodePen 这是CodePen的工作示例

And here is the code example: 这是代码示例:

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'));

And for sure, for this approach to work your only opportunity is to relay on state, instead of props of Component, which you can always set in componentWillReceiveProps method if you have to deal with them. 当然,对于这种工作方法,你唯一的机会是继续使用状态,而不是Component的道具,如果你必须处理它们,你可以随时在componentWillReceiveProps方法中设置它。

Updated 更新

Codepen link updated with more clear example which is showing benefits of this approach. 使用更清晰的示例更新了Codepen链接 ,该示例显示了此方法的优点。 Transition changed to javascript animation, without relying on transitionend event. 转换更改为javascript动画,而不依赖于transitionend事件。

You can add a class that describes the active state ie. 您可以添加描述活动状态的类,即。 .active and toggle that class when switching states. .active并在切换状态时切换该类。

The css should look something like this: css看起来应该是这样的:

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

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

As you want to render two different component for each of active and collapsed, wrap them in a div that controls the height with the help of CSS. 由于您想为每个活动和折叠渲染两个不同的组件,请将它们包装在一个div中,该div通过CSS帮助控制高度。

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

and in your CSS: 在你的CSS中:

.wrapper{
  transition: transform .5s linear;
}

.expanded{  
  height: 200px;
}

.collapsed{
  height: 20px;
}

Here you have a Toggle react component using the Velocity-React library, which is great for giving animations to transitions in React uis: 在这里你有一个使用Velocity-React库的Toggle反应组件,它非常适合为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,
};

Hope it helps! 希望能帮助到你!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM