学习笔记--方便下次查看
大多数情况下,在 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"
    }
});

 



作者:小人头11
链接:https://www.jianshu.com/p/08d24a5e7c06
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐