美文网首页
ReactNative 动画

ReactNative 动画

作者: 泓荥 | 来源:发表于2016-12-20 14:40 被阅读0次

    如何使用

    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}]
            )
    

    相关文章

      网友评论

          本文标题:ReactNative 动画

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