React.js 实现动画效果

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

    {SomeView}

// 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中添加在具体状态下的表现。

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

React.js 实现动画效果_第1张图片
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

源码部分如下:

React.js 实现动画效果_第2张图片
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 
This will animate!
; }, 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 实现动画效果)