ReactNative 动画

如何使用

import React, {Component} from 'react';
import {
    StyleSheet,
    View,
    Text,
    Animated,
    Easing
} from 'react-native';

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

        this.state = {
            fadeInOpacity: new Animated.Value(0)
        };
    }

    componentDidMount() {

        Animated.timing(this.state.fadeInOpacity, {
            toValue: 1, //  目标值
            duration: 2500, // 动画时间
            easing: Easing.linear  //  直线
        }).start();
    }

    render(){
        return(
            <Animated.View style={[styles.demo, {
                opacity: this.state.fadeInOpacity
            }]}>
                <Text style={styles.text}>悄悄的,我出现了</Text>
            </Animated.View>
        );
    }
}

const styles = StyleSheet.create({
    demo: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
        backgroundColor: 'white'
    },
    text: {
        fontSize: 30
    }
});

效果演示

Animated_Image.png

步骤拆解
一个RN的动画可以按照如下的步骤进行:

  • 使用基本的Animated组件,如Animated.View、Animated.Image、Animated.Text(目前RN只支持这三个组件,官网也提供了自定义Animated组件的方法,不过不推荐)
  • 使用Animated.Value设定一个或多个初始值(透明度、位置等)
  • 将初始化值绑定到动画目标的属性上(如Style)
  • 通过Animatd.timing等函数设定动画参数
  • 调用start启动动画

更复杂的例子

constructor(props){
        super(props);

        this.state = {
            fadeInOpacity: new Animated.Value(0),
            rotation: new Animated.Value(0),
            fontSize: new Animated.Value(0)
        };
    }

    componentDidMount() {
        Animated.parallel(['fadeInOpacity', 'rotation', 'fontSize'].map(index => {
            return Animated.timing(this.state[index], {
                toValue: 1,
                duration: 1000,
                easing: Easing.linear
            })
        })).start();
    }

    render(){
        return(
            <Animated.View style={[styles.demo, {
                opacity: this.state.fadeInOpacity,
                transform: [{
                    rotateZ: this.state.rotation.interpolate({
                        inputRange: [0, 1],
                        outputRange: ['0deg', '360deg']
                    })
                }]
            }]}>
                <Animated.Text style={{
                    fontSize: this.state.fontSize.interpolate({
                        inputRange: [0, 1],
                        outputRange: [12, 26]
                    })
                }}>我骑着七彩祥云出现了~~</Animated.Text>
            </Animated.View>
        );
    }

注意到我们使用了Animated的一个新方法,parallel,它表示同时执行一组动画

Paste_Image.png

强大的interpolate
上面的例子使用了interpolate函数,也就是插值函数。这个函数很强大,实现了数值大小、单位的映射转换,比如:

{
  inputRange: [0, 1],
  outputRange: [‘0deg’, '180deg']
}

当setValue(0.5)时,会自动映射成90deg。inputRange并不局限于[0, 1]区间,可以画出多段。interpolate一般用于多个动画公用一个Animated.Value,只需要在每个属性里面映射好对应的值,就可以用一个变量控制多个动画。事实上,上例中的Animated.Value可以用一个变量来声明,这里只是为了演示parallel的用法

流程控制
在刚才的例子中,我们使用了Parallel来实现多个动画并行渲染,其它用于流程控制的API还有:

  • sequence接受一系列动画数组为参数,并依次执行
  • stagger接受一系列动画数组和一个延迟时间,按照序列,每隔一个延迟时间后执行下一个动画(其实就是插入了delay的parrllel)
  • delay生成一个延时时间(基于timing的delay参数生成)
constructor(props){
        super(props);

        this.state = {
            anim: [1, 2, 3].map(() => new Animated.Value(0))    //  初始化3个值
        };
    }

    componentDidMount() {
        let timing = Animated.timing;
        Animated.sequence([
            Animated.stagger(200, this.state.anim.map(left => {
                return timing(left, {
                    toValue: 1,
                });
            }).concat(
                this.state.anim.map(left => {
                    return timing(left, {
                        toValue: 0,
                    });
                })
            )),     // 三个view滚到右边再还原,每个动作间隔200ms
            Animated.delay(400),        //  延迟400ms,配合sequence使用
            timing(this.state.anim[0], {
                toValue: 1,
            }),
            timing(this.state.anim[1], {
                toValue: -1,
            }),
            timing(this.state.anim[2], {
                toValue: 0.5
            }),
            Animated.delay(400),
            Animated.parallel(this.state.anim.map((anim) => {
                timing(anim, {
                    toValue: 0,
                })
            }))     //  同时回到原位
        ]).start();
    }

    render(){

        let views = this.state.anim.map((value, i) => {
            return (
                <Animated.View key={i}
                    style={[styles.demo, styles['demo' + 1], {
                        left: value.interpolate({
                            inputRange: [0, 1],
                            outputRange: [0, 200]
                        })
                    }]}>
                    <Text style={styles.text}>我是第{i + 1}个View</Text>
                </Animated.View>
            );
        });

        return(
            <View style={styles.container}>
                <Text>sequence/delay/stagger/paraller演示</Text>
                {views}
            </View>
        );
    }
Paste_Image.png

Spring/Decay/Timing
前面的几个动画都是基于时间实现的,事实上,在日常的手势操作中,基于时间的动画往往难以满足复杂的交互动画。对此,RN太提供了另外两种动画模式

  • Spring 弹簧效果
    • friction 摩擦系数,默认40
    • tension 张力系数,默认7
    • bounciness
    • speed
  • Decay 衰变效果
    • velocity 初速率
    • deceleration 衰减系数 默认0.997
      Spring支持friction与tension或者bounciness与speed两种组合模式,这两种模式不能并存。

Track && Event
RN动画支持跟踪功能,这也是日常交互中很常见的需求,比如跟踪用户的手势变化,跟踪另一个动画。而跟踪的用法也很简单,只需要指定toValue到另一个Animated.Value就可以了。交互动画需要跟踪用户的手势操作,Animated也很贴心地提供了事件借口的封装,如下:

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

推荐阅读更多精彩内容

  • React Native 进阶(二)--动画 动画 流畅、有意义的动画对于移动应用用户体验来说是非常必要的。我们可...
    呼呼哥阅读 2,747评论 2 5
  • 最近ReactNative(以下简称RN)在前端的热度越来越高,不少同学开始在业务中尝试使用RN,这里着重介绍一下...
    街角仰望阅读 4,999评论 6 6
  • 动画 Animated RN中的动画在某种程度上可以理解为“属性动画”,也就是以某种过渡方式改变组件样式属性值(如...
    尹_路人阅读 4,160评论 3 10
  • 刚踏入交互设计的新人,估计90%的人都被问过同一样的问题:什么是交互设计,请用一句话概述?很难做到一句话给说清楚,...
    小石头三枚阅读 5,998评论 10 18
  • 当红日被我的双眸所察觉时,我的内心充满了空虚的激昂! 当我懂得放弃的时候已经很累很累了,脚下的印迹已经很模糊了,尽...
    落寞的蓝阅读 488评论 4 9