React Native 动画(LayoutAnimation)

平时进行 Native 开发的时候,为了更好的用户体验,我们会做一些动画效果,而在 React Native 中也提供了相应的动画API。

React Native 主要的动画分为两大类:

  • LayoutAnimation 用来实现布局切换的动画。
  • Animated 更加精确的交互式的动画。

这篇文章主要说的是 LayoutAnimation 的用法,之后会写重点讲解 Animated 的文章。

LayoutAnimation

LayoutAnimation 是在布局发生变化时触发动画的接口,这意味着你需要通过修改布局(比如修改了组件的style、插入新组件)来触发动画。一个常用的调用此API的办法是调用 LayoutAnimation.configureNext,然后调用setState。

配置相关:

static configureNext(config: Config, onAnimationDidEnd?: Function) 
计划下一次布局要发生的动画。
@param config 表示动画相应的属性:
  duration 动画持续时间,单位是毫秒。
  create 配置创建新视图时的动画。
  update 配置被更新的视图的动画。

@param onAnimationDidEnd 当动画结束的时候被调用。只在iOS设备上支持。
@param onError 当动画产生错误的时候被调用。只在iOS设备上支持。

static create(duration: number, type, creationProp)
用来创建 configureNext 所需的 config 参数的辅助函数。

其中 config 参数格式参考:

LayoutAnimation.configureNext({
    duration: 700,   //动画持续时间
    create: {    //若是新布局的动画类型
        type: 'linear',  //线性模式,LayoutAnimation.Types.linear 写法亦可
        property: 'opacity'  //动画属性,除了opacity 还有一个 scaleXY 可以配置,LayoutAnimation.Properties.opacity 写法亦可
    },
    update: {  //若是布局更新的动画类型
        type: 'spring',   //弹跳模式
        springDamping: 0.4  //弹跳阻尼系数
    }
 });

create、update 的属性:

duration:动画持续时间(单位:毫秒),会覆盖 config 中设置的 duration。
delay:延迟指定时间(单位:毫秒)。
springDamping:弹跳动画阻尼系数(配合 spring 使用)。
initialVelocity:初始速度。

type:类型定义在LayoutAnimation.Types中:
spring:弹跳
linear:线性
easeInEaseOut:缓入缓出
easeIn:缓入
easeOut:缓出

property:类型定义在LayoutAnimation.Properties中:
opacity:透明度
scaleXY:缩放

示例:

import React, { Component } from 'react';
import {
    Platform,
    StyleSheet,
    Text,
    View,
    Image,
    TouchableOpacity,
    Dimensions,
    LayoutAnimation,
} from 'react-native';

var screenW = Dimensions.get('window').width;
var screenH = Dimensions.get('window').height;

export default class extends Component {
    constructor(props) {
        super(props)

        this.state = {
            width: 100,
            height: 150,
            left:20,
            top:20,
        }
    }

    _clickStartAnimation() {
        LayoutAnimation.configureNext({
            duration: 1000,   //持续时间
            create: {
                type: LayoutAnimation.Types.spring,
                property: 'opacity',
            },
            update: {
                type: 'spring',
                springDamping: 0.4,
            }
        });
        this.setState({
            width: this.state.width + 40,
            height: this.state.height + 60,
            left: this.state.left + 20,
            top: this.state.top + 50,
        });
    }

    render(){
        return (
            <View style={styles.mainStyle}>

                <Image style={{width:this.state.width,height:this.state.height,position:'absolute',left:this.state.left, top:this.state.top}}
                       source={{uri:'beauty.jpg'}}>
                </Image>

                <TouchableOpacity style={{width:200,height:50,backgroundColor:'yellow',marginTop:40}} onPress={this._clickStartAnimation.bind(this)}>
                    <Text style={{width:200,height:50,textAlign:'center',lineHeight:50}}>点击开始动画</Text>
                </TouchableOpacity>
            </View>
        );
    }
}

效果图:


效果图.gif

实现拆解:
在 update 中的 type 为 spring,每次点击按钮,图片都会改变宽高和位置,会有一个移动和缩放的动画效果。

人民币装入红包动画:

import React, { Component } from 'react';
import {
    Platform,
    StyleSheet,
    Text,
    View,
    Image,
    TouchableOpacity,
    Dimensions,
    LayoutAnimation,
} from 'react-native';

var anima = {
    duration: 1000,   //持续时间
    create: {
        type: LayoutAnimation.Types.spring,
        property: LayoutAnimation.Properties.scaleXY,
    },
    update: {
        type: 'easeOut',
    }
};

export default class extends Component {
    constructor(props) {
        super(props)

        this.state = {
            width: 200,
            height: 100,
            left:100,
            top:20,
        }
    }

    _clickStartAnimation() {

        LayoutAnimation.configureNext(anima,()=>{
            this.setState({
                top: 20,
            });
        });
        this.setState({
            top: this.state.top + 200,
        });
    }

    _setTimer(){
        // 先开始一次动画
        this._clickStartAnimation();
        // 创建定时器
        this.timer =  setInterval(this._clickStartAnimation.bind(this),1200);
    }

    render(){
        return (
            <View style={styles.mainStyle}>

                <Image style={{width:this.state.width,height:this.state.height,position:'absolute',left:this.state.left, top:this.state.top}}
                       source={{uri:'100.jpg'}}>
                </Image>

                <View style={{
                    width:240,
                    height:120,
                    position:'absolute',
                    left:80,
                    top:200,
                    backgroundColor:'red',
                }}>
                    <Text style={{color:'#FFD700',fontSize:90,lineHeight:104,width:240, textAlign:'center'}}>红包</Text>
                </View>

                <TouchableOpacity style={{width:200,height:50,backgroundColor:'yellow',marginTop:40}} onPress={this._setTimer.bind(this)}>
                    <Text style={{width:200,height:50,textAlign:'center',lineHeight:50}}>点击开始动画</Text>
                </TouchableOpacity>
            </View>
        );
    }
}

效果图:

红包效果图.gif

实现拆解:
首先创建一个 config 变量,1秒的持续时间,type 为 easeOut,然后在点击按钮之后,先执行一次动画,并同时开启定时器,1.2秒执行一次动画,在每次的动画执行完成之后,让100元回到原位,然后再次执行动画。

魔法现金动画:

import React, { Component } from 'react';
import {
    Platform,
    StyleSheet,
    Text,
    View,
    Image,
    TouchableOpacity,
    Dimensions,
    LayoutAnimation,
} from 'react-native';

var anima = {
    duration: 1000,   //持续时间
    create: {
        type: LayoutAnimation.Types.linear,
        property: LayoutAnimation.Properties.scaleXY,
    },
    update: {
        type: 'linear',
    }
};

export default class extends Component {
    constructor(props) {
        super(props)

        this.state = {
            width: 250,
            height: 125,
            show:false,
        }
    }

    _clickStartAnimation() {

        LayoutAnimation.configureNext(anima,()=>{});
        this.setState({
            show: true,
        });
    }

    render(){

        var secondMoney = this.state.show ? (
            <Image style={{width:this.state.width,height:this.state.height}}
                   source={{uri:'100.jpg'}}>
            </Image>
        ) : null;

        return (
            <View style={styles.mainStyle}>

                <Image style={{width:this.state.width,height:this.state.height}}
                       source={{uri:'100.jpg'}}>
                </Image>

                {secondMoney}

                <TouchableOpacity style={{width:200,height:50,backgroundColor:'yellow',marginTop:40}} onPress={this._clickStartAnimation.bind(this)}>
                    <Text style={{width:200,height:50,textAlign:'center',lineHeight:50}}>魔法现金</Text>
                </TouchableOpacity>
            </View>
        );
    }
}

效果图:

魔法现金效果图.gif

实现拆解:
首先创建一个 config 变量,1秒的持续时间,type 为 linear,property 为
scaleXY,然后在点击按钮之后,修改 state.show 为 true,然后根据这个属性就会加载出一个新的100元图片,因为是创建,不是更新,所以新的100元图片是执行的 create 内的动画效果,原来的100元图片和按钮则是执行的 update 效果。

LayoutAnimation.create

如果觉得上面的代码太繁杂,还可以通过 LayoutAnimation.create 这个函数更简单的创建一个config,也可以实现上面的动画效果。

create函数接受三个参数:

  • duration:动画持续时间。
  • type:create 和 update 时的动画类型,定义在 LayoutAnimation.Types。
  • creationProp:create 时的动画属性,定义 LayoutAnimation.Properties。

用法:

...
_clickStartAnimation() {
    LayoutAnimation.configureNext(LayoutAnimation.create(1000,
        LayoutAnimation.Types.linear,
        LayoutAnimation.Properties.scaleXY));
    this.setState({
        show: true,
    });
}
...

效果图和上面一样,参考上面即可。

LayoutAnimation.Presets

系统也为我们提供了3个默认的动画,定义在LayoutAnimation.Presets中:

  • easeInEaseOut:缓入缓出
  • linear:线性
  • spring:弹性

可以使用这种方式创建一些简单的动画效果。

用法:

...
_clickStartAnimation() {
    LayoutAnimation.configureNext(LayoutAnimation.Presets.linear);                
    this.setState({
        show: true,    
    });
}
...

这个效果和上面类似,区别在于这个默认 Properties 是 opacity,创建过程是透明度的变化。

效果图:


Presets效果图.gif

LayoutAnimation 使用总结:

  • LayoutAnimation 在 React Native 中可以很好的实现动画效果,完成可以和原生体验相媲美,非常值得大家一试。
  • 效果非常的流畅,而且动画的过程很柔和,丝毫没有生硬的感觉。
  • 可以灵活的配置动画参数,基本能满足单个动画的需求。
  • 在实现红包效果的时候,需要在动画完成之后执行一些操作,
    configureNext 第二个参数可以监听动画结束,从而完成这个需求,但是,这个参数只在 iOS 平台有效,ㄟ( ▔, ▔ )ㄏ。
  • 红包动画效果是在动画结束之后执行了一些操作,而如果动画的效果更为复杂,比如同时执行动画,再顺序执行,就会变的非常复杂。

Demo在这里,不麻烦的话 star 一下哈,谢谢!

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