美文网首页
React.js 实现动画效果

React.js 实现动画效果

作者: DiamondsAndRust | 来源:发表于2017-11-25 16:07 被阅读6843次

    React.js中的动画效果

    写在前面:
    目前网上搜到的有很多是讲 ReactCSSTransitionGroup 的,现在它已经被 CSSTransitionGroup 取代了,但是它们实现的机制是一样的。本文介绍的是CSSTransitionGroup。
    介绍两种,一种是渐变的过渡动画,一种是移动元素的间隔动画。

    一、过渡动画

    1.用什么——CSSTransitionGroup

    React通过CSSTransitionGroup可以实现动画效果。

    在之前版本中使用 ReactTransitionGroupReactCSSTransitionGroup 实现动画的,这两个包都被移动到了 react-transition-group 包中。

    2.怎么用

    一个元素渐隐渐现动画的简单例子:

    使用npm install react-transition-group --save导入react-transition-group

    import { CSSTransitionGroup } from 'react-transition-group';
    
    // JSX
    <CSSTransitionGroup 
      transitionName="example"
      transitionEnterTimeout={500} 
      transitionLeaveTimeout={300}>
        {SomeView}
    </CSSTransitionGroup>
    
    // CSS
    .example-enter {
        opacity: 0.01;
    }
      
    .example-enter.example-enter-active {
        opacity: 1;
        transition: opacity 200ms ease-in;
    }
      
    .example-leave {
        opacity: 1;
    }
      
    .example-leave.example-leave-active {
        opacity: 0.01;
        transition: opacity 200ms ease-in;
    }
    

    实现一个渐隐渐现的动画非常简单,在元素外部套上一个CSSTransitionGroup,对group进行一些设置,然后在CSS中对相应的类进行一些动画设置即可。

    3.这些属性都是什么

    首先看CSSTransitionGroup,它包含了以下几个属性(上例用到了其中三个):

    • transitionName

    它定义了这个动画组的名字。它可以是一个字符串,也可以是一个对象。如果是对象,则必须包含以下六个属性:

    transitionName={{
       enter:'itemEnter',
       leave:'itemLeave',
       appear:'itemAppear',
       enterActive:'itemEnterActive',
       leaveActive:'itemLeaveActive',
       appearActive:'itemAppearActive'
    }}
    

    在定制CSS动画的时候,如果transitionName是字符串,则类需要以它为前缀;如果transitionName为对象,则需要以对象中设置的为准。

    • transitionAppear / transitionEnter / transitionLeave

    这是三个BOOL值,代表是否要开启这个过渡动画。

    Appear是在元素挂载的时候的动画,Enter是元素出现时候的动画,Leave是元素消失时候的动画。

    • transitionAppearTimeout / tansitionEnterTimeout / transitionLeaveTimeout

    如果上一个BOOL值被设置成true,则这里必须设置与其对应的Timeout值,用于指定对应过渡动画的总时长。

    注意,这个timeout不是动画的准确时间,而是动画的的时长上限。如果动画时间小于timeout,则画面会呆住;如果动画时间大于timeout,则画面会在最后一帧直接跳到最终状态去。

    详细的说明一下动画时间小于timeout的情况:在Enter的时候,动画是从一开始就播放,多出来的时间在后面;Leave的时候,动画会在最后时刻播放完,多出来的时间在前面。

    这里就算设置了过渡动画存在,且设置了时间timeout,但是元素并不会自动的就生成一个什么动画效果。

    所以还需要在CSS中添加在具体状态下的表现。

    源码中对这几个属性具体的定义如下:

    proptypes.png

    4.是怎么做到的

    CSSTransitionGroup分为五个模块。

    TransitionEvents模块负责对transitioned,animationed时间进行绑定和解绑;

    ChildMapping提供了对 TransitionGroup 这个 component 的 children 进行格式化的工具;

    CSSTransitionGroup 会调用 CSSTransitionGroupChild 对 children 中的每个元素进行包装,然后将包装后的 children 作为 TransitionGroup 的 children

    TransitionGroup抽象appear,enter,leave三种动画过程,管理所有children的声明周期;

    怎么样可以实现对目标元素动画呢?

    CSSTransitionGroup的答案是对每个目标元素增加一组生命周期方法。

    当这个元素出现的回调方法中,给它添加一个class,实现一段动画;在这个元素消失的回调方法中,再给它添加一个类,实现消失动画。

    具体的来看:

    CSSTransitionGroup中把每个需要动画的元素格式化成了CSSTransitionGroupChild组件。

    对每个Child组件都实现三个方法:

    componentWillEnter

    componentWillLeave

    componentWillAppear

    源码部分如下:

    bind.png

    在这三个方法中,都调用一个transition方法,传入参数'enter', 'leave', 'appear',以及设置好的timeout等值。

    在transition方法中,会对传进来的字符串参数进行拼接:

    transitionName + 'appear'/'enter'/'leave'
    
    transitionName + 'appear'/'enter'/'leave' + 'active'
    

    然后将拼接好的类名添加到指定元素的DOM节点上,从而实现动画。

    源码如下:

    className.png

    开发者可以直接在CSS中按约定className编辑动画,而CSSTransitionGroup会识别到并展示出来。

    PS:劣势

    CSSTransitionGroup的优势是非常明显的,简化代码、提高性能等,但是其劣势我们也需要了解,以在做实际项目时进行适当的取舍。

    1. 不兼容较老的、不支持CSS3的浏览器;
    2. 不支持为CSS属性之外的东西(比如滚动条位置或canvas绘画)添加动画;
    3. 可控粒度不够细。CSS3动画只支持start、end、iteration三个事件,不支持对中间状态进行处理。
    4. transitionEnd和animationEnd事件不稳定。

    二、间隔动画

    间隔动画实现方式很简单,有两种:

    1. requestAnimationFrame
    2. setTimeout

    ​ requestAnimationFrame可以以最小的性能损耗实现最流畅的动画,它被调用的次数频繁度超出你想象。在requestAnimationFrame不支持或不可用的情况下,就要考虑降级到不那么智能的setTimeout了。

    ​ 间隔动画在实现原理上其实很简单,就是周期性的触发组件的状态更新,通过在组件的render方法中加入这个状态值,组件能够在每次状态更新触发的重渲染中正确表示当前的动态阶段。

    var Todo = React.createClass(
        getInitialState: function() {
            return {
                left: 0
            };
        },
        componentWillUpdate: function() {
            requestAnimationFrame(this. resolveAnimationFrame);
        },
        render: function() {
            return <div style={{left: this.state.left}}>This will animate!</div>;
        },
        resolveAnimationFrame: function() {
            if(this.state.left <= 100) {
                this.setState({
                    left: this.state.left + 1
                });
            }
        }
    );
    

    本文有很多内容参考了:
    http://www.alloyteam.com/2016/01/react-animation-practice/

    相关文章

      网友评论

          本文标题:React.js 实现动画效果

          本文链接:https://www.haomeiwen.com/subject/wjycbxtx.html