欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

react-native的Animated动画基本详解

程序员文章站 2024-01-14 17:14:28
...

一.介绍

动画类型:

spring:基础的单次弹跳物理模型
timing:从时间范围映射到渐变的值
decay:以一个初始速度开始并且逐渐减慢停止

创建动画的参数:

value:AnimatedValue | AnimatedValueXY(X轴或Y轴 | X轴和Y轴)
config:SpringAnimationConfig | TimingAnimationConfig | DecayAnimationConfig(动画的参数配置)

组件类型:

Animated.Text
Animated.Image
Animated.View:可以用来包裹任意视图
Animated.createAnimatedComponent():其它组件(较少用,用Animated.View包裹可以达到同样的效果)

import React, {Component} from 'react';

import {
    StyleSheet,
    View,
    Animated,
    Image,
    Easing
} from 'react-native';
import {Button} from "react-native-elements";

/**
 * @mark 文件内变量
 */

//文件内变量结束

export default class AnimatedDeomo extends React.Component {

    /***
     * default props value
     * @mark propTypes 默认属性值
     */
    static defaultProps = {}

    /***
     * props types for helper text
     * @mark propTypes 属性类型
     */
    static propTypes = {}

    /**
     * @mark state
     */
    state = {
        fadeOutOpacity: new Animated.Value(0),
        trans: new Animated.ValueXY({
            x: 0,
            y: 0
        }),
        rotation: new Animated.Value(0),
        scale: new Animated.Value(1),
        left: new Animated.Value(0),
    }

    /**
     * @mark constructor
     */
    constructor(props) {
        super(props);
    }

    /**
     * @mark 组件声明周期区域
     */

    /**
     * @mark 第一次加载 只运行一次
     */
    componentDidMount() {

    }

    //声明周期代码结束

    /**
     * @mark 自定义代码区
     */

    /**
     * @mark 淡入淡出
     */
    OpacityHandle(){
        this.startOpacity();
    }
    startOpacity() {
        Animated.timing(this.state.fadeOutOpacity, {
            toValue: 1,
            duration: 2000,
            easing: Easing.linear,// 线性的渐变函数
        }).start();
    }


    /**
     * @mark 位移
     */
    TransHandle(){
        this.startTrans();
    }
    startTrans() {
        Animated.timing(this.state.trans, {
            toValue: {
                x: 100,
                y: 100
            },
            duration: 2000,
        }).start();
    }
    /**
     * @mark 旋转并且持续进行动画
     */
    RotationHandle(){
        this.startRotation();
    }
    startRotation(){
        this.state.rotation.setValue(0);
        Animated.timing(this.state.rotation, {
            toValue: 1,        //属性目标值
            duration: 2000    //动画执行时间
        }).start(() => this.RotationHandle());   //执行动画
    }

    /**
     * @mark 缩放+spring摩擦力
     */
    ScaleHandle(){
        this.startScale();
    }
    startScale(){

        Animated.spring(this.state.scale, {
            toValue: 2,  //属性目标值
            duration: 500,
            friction: 5,        //摩擦力 (越小 振幅越大)
            tension: 1000,        //拉力
        }).start();            //执行动画
        /*Animated.timing(this.state.scale, {
            toValue: 2,
            duration: 500,
        }).start();*/
    }

    /**
     * @mark 滚动
     */
    LeftHandle(){
        this.startLeft();
    }
    startLeft(){
        Animated.timing(this.state.left, {
            toValue: 1,
            duration: 3000,
        }).start();
    }


    //自定义代码区结束

    /**
     * @mark render
     */
    render() {
        return <View style={styles.AnimatedDeomo}>
            <Animated.View // 可选的基本组件类型: Image, Text, View(可以包裹任意子View)
                style = {{alignItems: 'center',justifyContent: 'center',width:Theme.size.width,height:Theme.size.height/2,
                    opacity: this.state.fadeOutOpacity,

                    //可以修改成Left ,Right,top,进行位移转换
                    bottom:this.state.left.interpolate({
                        inputRange:[0,1],
                        outputRange:[0, Theme.size.width/2]
                    }),
                    transform:[
                        {
                            rotateX: this.state.rotation.interpolate({
                                inputRange:[0,1],
                                outputRange:['0deg','360deg']
                            })
                        },

                        {scale: this.state.scale}
                    ]}}>
                <Image resizeMode="cover" style={{width:40,height:40,}} source = {require("../../imgs/logo.png")}
                />
            </Animated.View >
            <Button
                fontSize={Theme.fontSize.max}
                buttonStyle={styles.loginBtn}
                onPress={this.OpacityHandle.bind(this)}
                title='淡入淡出'
            />
            <Button
                fontSize={Theme.fontSize.max}
                buttonStyle={styles.loginBtn}
                onPress={this.TransHandle.bind(this)}
                title='位移'
            />
            <Button
                fontSize={Theme.fontSize.max}
                buttonStyle={styles.loginBtn}
                onPress={this.RotationHandle.bind(this)}
                title='旋转'
            />
            <Button
                fontSize={Theme.fontSize.max}
                buttonStyle={styles.loginBtn}
                onPress={this.ScaleHandle.bind(this)}
                title='缩放'
            />
            <Button
                fontSize={Theme.fontSize.max}
                buttonStyle={styles.loginBtn}
                onPress={this.LeftHandle.bind(this)}
                title='滚动'
            />
        </View>
    }
}

//@mark style
const styles = StyleSheet.create({
    AnimatedDeomo: {
        flex:1
    },
    loginBtn:{
        marginTop:Theme.padding.less,
        backgroundColor:Theme.color.orange,
        borderRadius:Theme.radius.small
    }
});

通过上文的讲解,相信读者已经对如何用Animated创建动画有了最基本的认识。而有些时候,我们需要根据Scroll或者手势来手动的控制动画的过程。这就是我接下来要讲的。
手动控制动画的核心是Animated.event,
这里的Aniamted.event的输入是一个数组,用来做数据绑定
比如,

ScrollView中

onScroll={Animated.event(
           [{nativeEvent: {contentOffset: {x: this.state.xOffset}}}]//把contentOffset.x绑定给this.state.xOffset
)}
export default class ScrollAnimated extends React.Component {

    /***
     * default props value
     * @mark propTypes 默认属性值
     */
    static defaultProps = {}

    /***
     * props types for helper text
     * @mark propTypes 属性类型
     */
    static propTypes = {}

    /**
     * @mark state
     */
    state = {
        xOffset: new Animated.Value(0)
    }

    /**
     * @mark constructor
     */
    constructor(props) {
        super(props);
    }

    /**
     * @mark 组件声明周期区域
     */

    /**
     * @mark 第一次加载 只运行一次
     */
    componentDidMount() {

    }

    //声明周期代码结束

    /**
     * @mark 自定义代码区
     */


    //自定义代码区结束

    /**
     * @mark render
     */
    render() {
        return <View style={styles.ScrollAnimated}>

            <ScrollView
                        style={{height: Theme.size.height, width: Theme.size.width}}//设置大小
                        onScroll={Animated.event(
                            [{nativeEvent: {contentOffset: {y: this.state.xOffset}}}]//把contentOffset.x绑定给this.state.xOffset
                        )}
                        scrollEventThrottle={100}//onScroll回调间隔
            >

                <Image source={require('../../imgs/1.png')} style={{height: Theme.size.height, width: Theme.size.width}}
                       resizeMode="cover"/>
                <Image source={require('../../imgs/2.png')} style={{height: Theme.size.height, width: Theme.size.width}}
                       resizeMode="cover"/>
                <Image source={require('../../imgs/3.png')} style={{height: Theme.size.height, width: Theme.size.width}}
                       resizeMode="cover"/>
            </ScrollView>
            <Animated.View // 可选的基本组件类型: Image, Text, View(可以包裹任意子View)
                style = {[ styles.titleView,{ opacity:this.state.xOffset.interpolate({//映射到0.0,1.0之间
                    inputRange: [0,Theme.size.height],
                    outputRange: [0.0, 1.0]
                }),

                }]}>
                <Text style={{color:Theme.color.white,fontSize:15,textAlign:'center'}}>标题</Text>
            </Animated.View >
        </View>
    }
}

//@mark style
const styles = StyleSheet.create({
    ScrollAnimated: {
    },
    titleView:{
        alignItems: 'center',
        justifyContent: 'center',
        width:Theme.size.width,
        height:40,
        backgroundColor:Theme.color.orange,
        position: "absolute",
        top: 0,
        left: 0
    }
});