React Native动画Animated详解

在移动开发中,动画是提高用户体验不可缺少的一个元素。在React Native中,动画API提供了一些现成的组件:Animated.View,Animated.Text和Animated.Image默认支持动画。动画API会调用iOS或者Android的本地代码来完成这些组件的位移、大小等动画。

在React Native中,Animated创建过程如下:

  1. 创建Animated.Value,设置初始值,比如一个视图的opacity属性,最开始设置Animated.Value(0),来表示动画的开始时候,视图是全透明的。
  2. AnimatedValue绑定到Style的可动画属性,比如透明度,{opacity: this.state.fadeAnim}
  3. 使用Animated.timing来创建自动的动画,或者使用Animated.event来根据手势,触摸,Scroll的动态更新动画的状态(本文会侧重讲解Animated.timing)
  4. 调用Animated.timeing.start()开始动画

Animated简介

大多数情况下,在 React Native 中创建动画是推荐使用 Animated API 的,其提供了三个主要的方法用于创建动画:

  1. Animated.timing() -- 推动一个值按照一个过渡曲线而随时间变化。Easing 模块定义了很多缓冲曲线函数。
  2. Animated.decay() -- 推动一个值以一个初始的速度和一个衰减系数逐渐变为0。
  3. Animated.spring() -- 产生一个基于 Rebound 和 Origami 实现的Spring动画。它会在
    toValue 值更新的同时跟踪当前的速度状态,以确保动画连贯。

除了这三个创建动画的方法,对于每个独立的方法都有三种调用该动画的方式:

  1. Animated.parallel() --同时开始一个动画数组里的全部动画。默认情况下,如果有任何一个动画停止了,其余的也会被停止。你可以通过stopTogether 选项来改变这个效果。
  2. Animated.sequence() --按顺序执行一个动画数组里的动画,等待一个完成后再执行下一个。如果当前的动画被中止,后面的动画则不会继续执行。
  3. Animated.stagger() -- 一个动画数组,里面的动画有可能会同时执行(重叠),不过会以指定的延迟来开始。

Animated.timing()

使用 Animated.timing 创建的旋转动画。Animated.timing()的基本使用方法如下:

Animated.timing(
  someValue,
  {
    toValue: number,
    duration: number,
    easing: easingFunction,
    delay: number
  }
)

Easing 也是用React Native创建动画的载体,它允许我们使用已经定义好的各种缓冲函数,例如:linear, ease, quad, cubic, sin, elastic, bounce, back, bezier, in, out, inout 。由于有直线运动,我们将使用 linear。
接下来,需要在构造函数中初始化一个带动画属性的值用于旋转动画的初始值:

constructor () {
  super()
  this.spinValue = new Animated.Value(0)
}

我们使用 Animated.Value声明了一个 spinValue 变量,并传了一个 0 作为初始值。然后创建了一个名为 spin 的方法,并在 componentDidMount 中调用它,目的是在 app 加载之后运行动画。

componentDidMount () {
  this.spin()
}
spin () {
  this.spinValue.setValue(0)
  Animated.timing(
    this.spinValue,
    {
      toValue: 1,
      duration: 4000,
      easing: Easing.linear
    }
  ).start(() => this.spin())
}

现在方法已经创建好了,接下来就是在UI中渲染动画了。

render () {
  const spin = this.spinValue.interpolate({
    inputRange: [0, 1],
    outputRange: ['0deg', '360deg']
  })
  return (
    <View style={styles.container}>
      <Animated.Image
        style={{
          width: 227,
          height: 200,
          transform: [{rotate: spin}] }}
          source={{uri: 'https://s3.amazonaws.com/media-p.slid.es/uploads/alexanderfarennikov/images/1198519/reactjs.png'}}
      />
    </View>
  )
}

实现效果:

完整代码:

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

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

class AnimationRotateScene extends Component {

    constructor(props) {
        super(props);
        this.spinValue = new Animated.Value(0)
    }

    componentDidMount () {
        this.spin()
    }

    spin () {
        this.spinValue.setValue(0)
        Animated.timing(
            this.spinValue,
            {
                toValue: 1,
                duration: 4000,
                easing: Easing.linear
            }
        ).start(() => this.spin())
    }

    render() {

        const
            spin = this.spinValue.interpolate({
                inputRange: [0, 1],
                outputRange: ['0deg', '360deg']
            })

        return (
            <View style={styles.container}>

                <Animated.Image
                    style={{
                        width: 227,
                        height: 200,
                        transform: [{rotate: spin}] }}
                    source={{uri: 'https://s3.amazonaws.com/media-p.slid.es/uploads/alexanderfarennikov/images/1198519/reactjs.png'}}
                />
                <TouchableOpacity onPress={() => this.spin()} style={styles.button}>
                    <Text>启动动画</Text>
                </TouchableOpacity>
            </View>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        marginTop: 20,
        justifyContent: 'center',
        alignItems: 'center',
    },
    button: {
        marginTop: 20,
        backgroundColor:'#808080',
        height:35,
        width:140,
        borderRadius:5,
        justifyContent: 'center',
        alignItems: 'center',
    },
});

export default AnimationRotateScene;

Animated.spring()

使用 Animated.spring() 方法创建一个放大缩小的动画。

Animated.spring() 方法使用:

Animated.spring(
    someValue,
    {
      toValue: number,
      friction: number
    }
)

如上图所示,我们要使用Animated.spring()创建一个放大缩小的动画效果。
在构造函数中,创建一个 springValue 变量,初始化其值为0.3。

constructor () {
  super()
  this.springValue = new Animated.Value(0.3)
}

然后,删除 animated 方法和componentDidMount方法,创建一个新的 spring 方法。

spring () {
  this.springValue.setValue(0.3)
  Animated.spring(
    this.springValue,
    {
      toValue: 1,
      friction: 1
    }
  ).start()
}

然后我们给View的button添加一个点击事件,出发上面的spring动画。

<View style={styles.container}>
  <Text
    style={{marginBottom: 100}}
    onPress={this.spring.bind(this)}>Spring</Text>
    <Animated.Image
      style={{ width: 227, height: 200, transform: [{scale: this.springValue}] }}
      source={{uri: 'https://s3.amazonaws.com/media-p.slid.es/uploads/alexanderfarennikov/images/1198519/reactjs.png'}}/>
</View>

完整代码如下:

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

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

class AnimationRotateScene extends Component {

    constructor(props) {
        super(props);
        this.spinValue = new Animated.Value(0)
    }

    componentDidMount () {
        this.spin()
    }

    spin () {
        this.spinValue.setValue(0)
        Animated.timing(
            this.spinValue,
            {
                toValue: 1,
                duration: 4000,
                easing: Easing.linear
            }
        ).start(() => this.spin())
    }

    render() {

        const
            spin = this.spinValue.interpolate({
                inputRange: [0, 1],
                outputRange: ['0deg', '360deg']
            })

        return (
            <View style={styles.container}>

                <Animated.Image
                    style={{
                        width: 227,
                        height: 200,
                        transform: [{rotate: spin}] }}
                    source={{uri: 'https://s3.amazonaws.com/media-p.slid.es/uploads/alexanderfarennikov/images/1198519/reactjs.png'}}
                />
                <TouchableOpacity onPress={() => this.spin()} style={styles.button}>
                    <Text>启动动画</Text>
                </TouchableOpacity>
            </View>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        marginTop: 20,
        justifyContent: 'center',
        alignItems: 'center',
    },
    button: {
        marginTop: 20,
        backgroundColor:'#808080',
        height:35,
        width:140,
        borderRadius:5,
        justifyContent: 'center',
        alignItems: 'center',
    },
});

export default AnimationRotateScene;

Animated.parallel()

Animated.parallel() 会同时开始一个动画数组里的全部动画。parallel()会接受一个动画数组,首先看一下api:

Animated.parallel(arrayOfAnimations)
// In use:
Animated.parallel([
  Animated.spring(
    animatedValue,
    {
      //config options
    }
  ),
  Animated.timing(
     animatedValue2,
     {
       //config options
     }
  )
])

所以,我们先创建一个动画数组,并初始化。

constructor () {
  super()
  this.animatedValue1 = new Animated.Value(0)
  this.animatedValue2 = new Animated.Value(0)
  this.animatedValue3 = new Animated.Value(0)
}

然后,创建一个 animate 方法并在 componendDidMount() 中调用它。

componentDidMount () {
  this.animate()
}
animate () {
  this.animatedValue1.setValue(0)
  this.animatedValue2.setValue(0)
  this.animatedValue3.setValue(0)
  const createAnimation = function (value, duration, easing, delay = 0) {
    return Animated.timing(
      value,
      {
        toValue: 1,
        duration,
        easing,
        delay
      }
    )
  }
  Animated.parallel([
    createAnimation(this.animatedValue1, 2000, Easing.ease),
    createAnimation(this.animatedValue2, 1000, Easing.ease, 1000),
    createAnimation(this.animatedValue3, 1000, Easing.ease, 2000)
  ]).start()
}

在 animate 方法中,我们将三个动画属性值重置为0。此外,还创建了一个 createAnimation 方法,该方法接受四个参数:value, duration, easing, delay(默认值是0),返回一个新的动画。

然后,调用 Animated.parallel(),并将三个使用 createAnimation 创建的动画作为参数传递给它。在 render 方法中,我们需要设置插值:

render () {
  const scaleText = this.animatedValue1.interpolate({
    inputRange: [0, 1],
    outputRange: [0.5, 2]
  })
  const spinText = this.animatedValue2.interpolate({
    inputRange: [0, 1],
    outputRange: ['0deg', '720deg']
  })
  const introButton = this.animatedValue3.interpolate({
    inputRange: [0, 1],
    outputRange: [-100, 400]
  })
  ...
}

最后,我们用一个主 View 包裹三个 Animated.Views:

<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>

完整的代码如下:

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow 组动画
 */

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

class AnimationGroupScene extends Component {

    constructor() {
        super()
        this.animatedValue1 = new Animated.Value(0)
        this.animatedValue2 = new Animated.Value(0)
        this.animatedValue3 = new Animated.Value(0)
    }

    componentDidMount() {
        this.animate()
    }

    animate() {
        this.animatedValue1.setValue(0)
        this.animatedValue2.setValue(0)
        this.animatedValue3.setValue(0)
        const createAnimation = function (value, duration, easing, delay = 0) {
            return Animated.timing(
                value,
                {
                    toValue: 1,
                    duration,
                    easing,
                    delay
                }
            )
        }
        Animated.parallel([
            createAnimation(this.animatedValue1, 2000, Easing.ease),
            createAnimation(this.animatedValue2, 1000, Easing.ease, 1000),
            createAnimation(this.animatedValue3, 1000, Easing.ease, 2000)
        ]).start()
    }

    startAnimation() {
        this.state.currentAlpha = this.state.currentAlpha == 1.0 ? 0.0 : 1.0;
        Animated.timing(
            this.state.fadeAnim,
            {toValue: this.state.currentAlpha}
        ).start();
    }

    render() {

        const scaleText = this.animatedValue1.interpolate({
            inputRange: [0, 1],
            outputRange: [0.5, 2]
        })
        const spinText = this.animatedValue2.interpolate({
            inputRange: [0, 1],
            outputRange: ['0deg', '720deg']
        })
        const introButton = this.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>启动组动画</Text>
                    </TouchableHighlight>
                </Animated.View>

            </View>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        marginTop: 20,
        justifyContent: 'center',
        alignItems: 'center',
    },
    button: {
        marginTop: 20,
        backgroundColor: '#808080',
        height: 35,
        width: 140,
        borderRadius: 5,
        justifyContent: 'center',
        alignItems: 'center',
    },
});

export default AnimationGroupScene;

示例使用说明



如图所示,我对动画的代码做了一个简单的整理,大家在使用的时候直接引入AnimationRoot文件即可。
AnimationRoot文件内容如下:

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, {Component} from 'react';
import { StackNavigator } from 'react-navigation';

import AnimationIndex from './AnimationIndex';
import AnimationSpringScene from './AnimationSpringScene';//缩放动画
import AnimationRotateScene from './AnimationRotateScene';//旋转动画
import AnimationAlphaScene from './AnimationAlphaScene';//Alpha动画
import AnimationGroupScene from './AnimationGroupScene';//组动画
import AnimationFrameScene from './AnimationFrameScene';//帧动画

const anim = StackNavigator({
    AnimationIndex: { screen: AnimationIndex },
    AnimationSpringScene: { screen: AnimationSpringScene },
    AnimationRotateScene: { screen: AnimationRotateScene },
    AnimationAlphaScene: { screen: AnimationAlphaScene },
    AnimationGroupScene: { screen: AnimationGroupScene },
    AnimationFrameScene: { screen: AnimationFrameScene },
});
export default anim;

最后是项目实现的最终结果图,代码地址动画源码

时间: 2024-10-14 09:01:17

React Native动画Animated详解的相关文章

Android Animations动画使用详解

一.动画类型 Android的animation由四种类型组成:alpha.scale.translate.rotate XML配置文件中 二. Android动画模式 Animation主要有两种动画模式:tweened 和 frame 一种是tweened animation(渐变动画) 三.XML文件中定义动画 ① 打开Eclipse,新建Android工程 ② 在res目录中新建anim文件夹 ③ 在anim目录中新建一个myanim.xml(注意文件名小写) ④ 加入XML的动画代码

POPSpring动画参数详解

POPSpring动画参数详解   效果   源码 https://github.com/YouXianMing/Animations // // POPSpringParameterController.m // Animations // // Created by YouXianMing on 15/11/29. // Copyright 2015年 YouXianMing. All rights reserved. // #import "POPSpringParameterContro

Android Dialog 动画实例详解

Android Dialog 动画实例详解 动画描述: 动画与底部菜单一样出现和消失 制作过程: 1. 创建两个动画文件 window_in.xml: <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" > <translate android:duration=&

AngularJS 实现JavaScript 动画效果详解_AngularJS

AngularJS 应用中实现 JavaScript 动画效果 AngularJS 是一组用于创建单页Web应用的丰富框架,给构建丰富交互地应用带来了所有需要的功能.其中一项主要的特性就是Angular带来了对动画的支持. 我们能够在应用的部分内容当中使用动画来表明一些变化正在发生.在我上一篇文章当中,我讲到了在Angular应用中对CSS动画的支持.在这篇文章当中,我们会看到怎样利用JavaScript脚本在AngularJS应用当中生成动画效果. 在Angular当中,CSS和JavaScr

Android实现动画效果详解_Android

目前Android平台提供了两类动画一类是Tween动画,第二类就是 Frame动画,具体内容介绍请看下文: 一类是Tween动画,就是对场景里的对象不断的进行图像变化来产生动画效果(旋转.平移.放缩和渐变). 第二类就是 Frame动画,即顺序的播放事先做好的图像,与gif图片原理类似. 实现动画有两种方式:一种使用XML文件(文件放在res/anim),一种直接代码搞定  1.透明度控制动画效果alpha <!-- 透明度控制动画效果alpha 浮点型值: fromAlpha 动画起始时透明

Android帧动画、补间动画、属性动画用法详解_Android

在安卓开发中,经常会使用到一些动画,那么在开发中,如何使用这些动画呢? 帧动画:不是针对View做出一些形状上的变化,而是用于播放一张张的图片,例如一些开机动画,类似于电影播放,使用的是AnimationDrawable来播放帧动画 res/drawable  <?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:android="http://schemas.andro

Swift自动布局库SnapKit的约束优先级,约束做动画使用详解

1,约束优先级 我们使用SnapKit的时候,还可以定义约束的优先级.这样当约束出现冲突的时候,优先级高的约束覆盖优先级低的约束.具体优先级可以放在约束链的结束处. (1)可以设置如下几种优先级 priorityLow():设置低优先级,优先级为250 priorityMedium():设置中优先级,优先级为500(这个也就是默认的优先级) priorityHigh():设置高优先级,优先级为750 priority():可以设置任意的优先级,接受的参数是0-1000的数字.比如:priorit

Android实现动画效果详解

目前Android平台提供了两类动画一类是Tween动画,第二类就是 Frame动画,具体内容介绍请看下文: 一类是Tween动画,就是对场景里的对象不断的进行图像变化来产生动画效果(旋转.平移.放缩和渐变). 第二类就是 Frame动画,即顺序的播放事先做好的图像,与gif图片原理类似. 实现动画有两种方式:一种使用XML文件(文件放在res/anim),一种直接代码搞定 1.透明度控制动画效果alpha <!-- 透明度控制动画效果alpha 浮点型值: fromAlpha 动画起始时透明度

Android帧动画、补间动画、属性动画用法详解

在安卓开发中,经常会使用到一些动画,那么在开发中,如何使用这些动画呢? 帧动画:不是针对View做出一些形状上的变化,而是用于播放一张张的图片,例如一些开机动画,类似于电影播放,使用的是AnimationDrawable来播放帧动画 res/drawable <?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:android="http://schemas.androi