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
<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/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容