美文网首页React Native学习ReactNative
React Native 动画(Animated)笔记

React Native 动画(Animated)笔记

作者: 小小小小的人头 | 来源:发表于2019-07-15 17:54 被阅读14次

    学习笔记--方便下次查看
    大多数情况下,在 React Native 中创建动画是推荐使用 Animated API 的,其提供了三个主要的方法用于创建动画:

    1.API

    1. Animated.timing() -- 最常用的动画类型,使一个值按照一个过渡曲线而随时间变化。
    2. Animated.decay() -- 衰变效果,以一个初始的速度和一个衰减系数逐渐减慢变为0。
    3. Animated.spring() -- 弹簧效果,基础的单次弹跳物理模型实现的 spring 动画

    2.动画组件

    Animated 封装了四个可以动画化的组件:
    Animated.View、Animated.Text、Animated.Image、Animated.ScrollView
    动画必须依赖这几个组件--使用当然和平常开发一样

    4.Animated.timing()的使用

    最常用的动画--我们看如何使用吧;
    这边是一个渐变的效果;


    渐变效果.gif
    import React from "react";
    import { Animated } from "react-native";
    
    export default class FadeInView extends React.Component {
      state = {
        fadeInOpacity: new Animated.Value(0) // 透明度初始值设为0
      };
      componentDidMount() {
        Animated.timing(
          // 随时间变化而执行动画
          this.state.fadeInOpacity, // 动画中的变量值
          {
            toValue: 1, // 透明度最终变为1,即完全不透明
            duration: 6000 // 让动画持续一段时间
          }
        ).start();
      }
      render() {
        const { fadeInOpacity } = this.state;
        return (
          <Animated.View // 使用专门的可动画化的View组件
            style={{
              width: 100,
              height: 100,
              backgroundColor: "red",
              opacity: fadeInOpacity // 将透明度指定为动画变量值
            }}
          />
        );
      }
    }
    
    

    这个demo 简单说一下代码的意思--方便理解

    • 在state中初始化一个动画效果值fadeInOpacity为0
    • 在生命周期中-使用动画的函数timing- 让fadeInOpacity在6秒钟从0变成1
    • fadeInOpacity的值使用在render里面控制opacity的值
      从而达到了一个渐变的效果

    Animated.timing中的toValue配置项不一定是配置成1,也可以配置成其他数字比如控制一个view的宽度从0到100的过程,也可以试一下;

    3.插值函数(interpolate)

    interpolate():将输入值范围转换为输出值范围。
    譬如:把0-1映射到0-10。
    接下来,我们结合一个个的例子介绍它们的用法-当初自己学习挺懵逼的-先看demo


    旋转.gif
    import { Animated, Easing, StyleSheet, View } from "react-native";
    
    export default class Rotate extends React.Component<any, any> {
      state = {
        spinValue: new Animated.Value(0) //初始化动画的状态值
      };
      componentDidMount() {
        Animated.timing(this.state.spinValue, { //动画函数
          toValue: 1,                           //渐变的值
          duration: 3000,                        //时间
        }).start();
      }
    
      render() {
        const spin = this.state.spinValue.interpolate({
          inputRange: [0, 1],   //入参---是从0变化到1
          outputRange: ["0deg", "360deg"]   //出参---是从0deg变化到360deg
        });
        return (
          <View style={styles.container}>
            <Animated.Image
              style={{
                width: 300,
                height: 300,
                transform: [{ rotate: spin }]
              }}
              resizeMode={"contain"}
              source={{
                uri:
                  "https://s3.amazonaws.com/media-p.slid.es/uploads/alexanderfarennikov/images/1198519/reactjs.png"
              }}
            />
          </View>
        );
      }
    }
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        alignItems: "center",
        justifyContent: "center"
      }
    });
    

    这是一个旋转的动画效果--代码和上文的渐变效果其实基本一致--唯一不同的是使用了interpolate 需要注意的是对inputRangeoutputRange的理解:动画属性值有一个从0到1的过程,我们其他属性也需要一个这样的过程,比如动画从0-360的旋转过程。那就需要一个映射的过程了。动画属性值从0到1 那我们旋转就从0-360。从而达到动画的效果了;

    4.1interpolate的其他使用方式

    inputRange是从0-1增长的过程。当然我们可以设置成多段的比如[0,0.5,1] ,那我们outputRange的也可以映射自己想要的过程。比如过程编导0.5的的时候,我想让他逆时针旋转,我们outputRange就可以写成这样[0,'360deg',0] 下面看下各种demo

    多端设置组合.gif
    import React from "react";
    import { Animated, Easing, StyleSheet, View, Text } from "react-native";
    export default class FadeInView extends React.Component<any, any> {
      constructor(props) {
        super(props);
        this.state = {
          animatedValue: new Animated.Value(0)
        };
      }
    
      componentDidMount() {
        this.spin();
      }
    
      spin() {
        this.state.animatedValue.setValue(0);
        Animated.timing(this.state.animatedValue, {
          toValue: 1,
          duration: 2000,
          easing: Easing.linear
          // delay:1000
        }).start(() => this.spin());
      }
    
      render() {
        let { animatedValue } = this.state;
        const marginLeft = animatedValue.interpolate({
          inputRange: [0, 1],
          outputRange: [0, 100]
        });
        const opacity = animatedValue.interpolate({
          inputRange: [0, 0.5, 1],
          outputRange: [0, 1, 0]
        });
        const movingMargin = animatedValue.interpolate({
          inputRange: [0, 0.5, 1],
          outputRange: [0, 300, 0]
        });
        const textSize = animatedValue.interpolate({
          inputRange: [0, 0.5, 1],
          outputRange: [18, 32, 18]
        });
        const rotateX = animatedValue.interpolate({
          inputRange: [0, 0.5, 1],
          outputRange: ["0deg", "180deg", "0deg"]
        });
        return (
          <View style={styles.container}>
            <Animated.View
              style={{
                marginLeft,
                height: 30,
                width: 40,
                backgroundColor: "red"
              }}
            />
            <Animated.View
              style={{
                opacity,
                marginTop: 10,
                height: 30,
                width: 40,
                backgroundColor: "blue"
              }}
            />
            <Animated.View
              style={{
                marginLeft: movingMargin,
                marginTop: 10,
                height: 30,
                width: 40,
                backgroundColor: "orange"
              }}
            />
            <Animated.Text
              style={{
                fontSize: textSize,
                marginTop: 10,
                color: "green"
              }}
            >
              Animated Text!
            </Animated.Text>
            <Animated.View
              style={{
                transform: [{ rotateX }],
                marginTop: 50,
                height: 30,
                width: 40,
                backgroundColor: "black"
              }}
            >
              <Text style={{ color: "white" }}>Hello from TransformX</Text>
            </Animated.View>
          </View>
        );
      }
    }
    const styles = StyleSheet.create({
      container: {
        flex: 1
      }
    });
    

    我们可以用动画的值用在opacity、margins、text sizes 和 rotation 等样式属性中。就出现了上面的运动旋转等动画了

    5.start()循环播放

    如何实现动画的持续播放呢--那就需要用到我们start()的回调中调用自己,当然他是在动画结束后才会调用的-这个过程让我想到了 递归

      spin() {
        this.state.animatedValue.setValue(0);
        Animated.timing(this.state.animatedValue, {
         .....
        }).start(() => this.spin());
      }
    

    tips:有一个小细节就this.state.animatedValue.setValue(0);这一步。动画结束后需要将初始值变成最初的值因为他有一个0-1的渐变过程。所以需要重置一下变量;

    6.Animated.spring()

    这个就是弹框效果了--直接看代码吧。使用都一样的


    弹簧效果.gif
    import React from "react";
    import { Animated, StyleSheet, Text, View } from "react-native";
    export default class FadeInView extends React.Component<any, any> {
      constructor(props) {
        super(props);
        this.state = {
          springValue: new Animated.Value(0)
        };
      }
    
      componentDidMount() {
        this.spring();
      }
    
      spring() {
        this.state.springValue.setValue(0);
        Animated.spring(this.state.springValue, {
          toValue: 1,               //放大的值:越大放大越大
          friction: 1,              //摩擦力:越大就越不能晃动的厉害
          tension:1                     //张力:越大就越有弹性-生动点
        }).start();
      }
    
      render() {
        return (
          <View style={styles.container}>
            <Text
              style={{ marginBottom: 100 }}
              onPress={() => {
                this.spring();
              }}
            >
              点击可以出发动画
            </Text>
    
            <Animated.Image
              style={{
                width: 227,
                height: 200,
                transform: [{ scale: this.state.springValue }]
              }}
              source={{
                uri:
                  "https://s3.amazonaws.com/media-p.slid.es/uploads/alexanderfarennikov/images/1198519/reactjs.png"
              }}
            />
          </View>
        );
      }
    }
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        alignItems: "center",
        justifyContent: "center"
      }
    });
    

    里面有一些属性需要注意一下--
    toValue: 放大的值:越大放大越大
    friction: 摩擦力:越大就越不能晃动的厉害
    tension: 张力:越大就越有弹性-生动点

    这边我好想吐槽一句--记笔记真的烦人--

    7.Animated.parallel()

    Animated.parallel() 会同时开始一个动画数组里的全部动画。


    多个动画同时执行.gif
    import React from "react";
    import {
      Animated,
      Easing,
      StyleSheet,
      Text,
      TouchableHighlight,
      View
    } from "react-native";
    export default class FadeInView extends React.Component<any, any> {
      constructor(props) {
        super(props);
        this.state = {
          animatedValue1: new Animated.Value(0),
          animatedValue2: new Animated.Value(0),
          animatedValue3: new Animated.Value(0)
        };
      }
    
      componentDidMount() {
        this.animate();
      }
    
      animate() {
        this.state.animatedValue1.setValue(0);
        this.state.animatedValue2.setValue(0);
        this.state.animatedValue3.setValue(0);
        const createAnimation = function(value, duration, easing, delay = 0) {
          return Animated.timing(value, {
            toValue: 1,
            duration,
            easing,
            delay
          });
        };
        Animated.parallel([
          createAnimation(this.state.animatedValue1, 2000, Easing.ease),
          createAnimation(this.state.animatedValue2, 1000, Easing.ease, 1000),
          createAnimation(this.state.animatedValue3, 1000, Easing.ease, 2000)
        ]).start();
      }
    
      spring() {
        this.state.springValue.setValue(0.3);
        Animated.spring(this.state.springValue, {
          toValue: 1,
          friction: 1
          // tension:1
        }).start();
      }
    
      render() {
        const scaleText = this.state.animatedValue1.interpolate({
          inputRange: [0, 1],
          outputRange: [0.5, 2]
        });
        const spinText = this.state.animatedValue2.interpolate({
          inputRange: [0, 1],
          outputRange: ["0deg", "720deg"]
        });
        const introButton = this.state.animatedValue3.interpolate({
          inputRange: [0, 1],
          outputRange: [-100, 400]
        });
        return (
          <View style={[styles.container]}>
            <Animated.View style={{ transform: [{ scale: scaleText }] }}>
              <Text>Welcome</Text>
            </Animated.View>
            <Animated.View
              style={{ marginTop: 20, transform: [{ rotate: spinText }] }}
            >
              <Text style={{ fontSize: 20 }}>to the App!</Text>
            </Animated.View>
            <Animated.View style={{ top: introButton, position: "absolute" }}>
              <TouchableHighlight
                onPress={this.animate.bind(this)}
                style={styles.button}
              >
                <Text style={{ color: "white", fontSize: 20 }}>
                  Click Here To Start
                </Text>
              </TouchableHighlight>
            </Animated.View>
          </View>
        );
      }
    }
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        alignItems: "center",
        justifyContent: "center"
      },
      button: {
        width: 320,
        height: 90,
        backgroundColor: "blue",
        alignItems: "center",
        justifyContent: "center"
      }
    });
    

    8.Animated.parallel()

    和 Animated.parallel() 一样, Animated.sequence() 接受一个动画数组。但不同的是,Animated.sequence() 是按顺序执行一个动画数组里的动画,等待一个完成后再执行下一个。


    一个个输出播放.gif
    import React from "react";
    import { Animated, StyleSheet, View } from "react-native";
    const arr = [];
    for (let i = 0; i < 500; i++) {
      arr.push(i);
    }
    export default class FadeInView extends React.Component<any, any> {
      constructor(props) {
        super(props);
        this.state = {
          animatedValue: []
        };
        arr.forEach(value => {
          this.state.animatedValue[value] = new Animated.Value(0);
        });
      }
      componentDidMount() {
        this.animate();
      }
      animate() {
        const animations = arr.map(item => {
          return Animated.timing(this.state.animatedValue[item], {
            toValue: 1,
            duration: 100
          });
        });
    
        Animated.sequence(animations).start();
      }
    
      render() {
        const animations = arr.map((a, i) => {
          return (
            <Animated.View
              key={i}
              style={{
                opacity: this.state.animatedValue[a],
                height: 20,
                width: 20,
                backgroundColor: "red",
                marginLeft: 3,
                marginTop: 3
              }}
            />
          );
        });
    
        return <View style={styles.container}>{animations}</View>;
      }
    }
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        flexDirection: "row",
        flexWrap: "wrap"
      }
    });
    
    

    9. Animated.Stagger()

    和 Animated.parallel() 和 Animated.sequence() 一样, Animated.Stagger 接受一个动画数组。但不同的是,Animated.Stagger 里面的动画有可能会同时执行(重叠),不过会以指定的延迟来开始。与上述两个动画主要的不同点是 Animated.Stagger 的第一个参数,delay 会被应用到每一个动画

    可以同时播放的.gif
    import React, {Component} from "react";
    
    import {Animated, StyleSheet, View} from "react-native";
    
    const arr = [];
    for (let i = 0; i < 200; i++) {
        arr.push(i);
    }
    export default class animations extends Component<any, any> {
        constructor(props) {
            super(props);
            this.state = {
                animatedValue: []
            };
    
            arr.forEach(value => {
                this.state.animatedValue[value] = new Animated.Value(0);
            });
        }
    
        componentDidMount() {
            this.animate();
        }
    
        animate() {
            this.state.animatedValue.map(c=>{
                c.setValue(0);
            })
            const animations = arr.map(item => {
                return Animated.timing(this.state.animatedValue[item], {
                    toValue: 1,
    
                    duration: 1000
                });
            });
    
            Animated.stagger(10, animations).start(()=>{this.animate()});
        }
    
        render() {
            const animations = arr.map((a, i) => {
                return (
                    <Animated.View
                        key={i}
                        style={{
                            opacity: this.state.animatedValue[a].interpolate({
                                inputRange: [0, 0.5, 1],
                                outputRange: [0, 1, 0.3]
                            }),
                            height: 20,
                            width: 20,
                            backgroundColor: "red",
                            marginLeft: 3,
                            marginTop: 3
                        }}
                    />
                );
            });
    
            return <View style={styles.container}>{animations}</View>;
        }
    }
    
    const styles = StyleSheet.create({
        container: {
            flex: 1,
            flexDirection: "row",
            flexWrap: "wrap"
        }
    });
    
    

    相关文章

      网友评论

        本文标题:React Native 动画(Animated)笔记

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