1. 程式人生 > >React Native Animated動畫

React Native Animated動畫

在React Native中,我們可以通過兩種方式實現一個動畫效果:

  • LayoutAnimation
  • Animated

關於LayoutAnimation,我之前寫過一篇學習部落格(React Native LayoutAnimation動畫)。此主要用於在頁面佈局改變的時候新增一些動畫效果,但如果想要實現一些更精細,複雜的動畫,LayoutAnimation就會比較困難,所以React Native還為我們提供了Animated元件,用來實現一些複雜的動畫效果。

一、單個動畫

Animated為我們提供了三種類型的動畫:spring,timing,decay。

1、Spring 彈跳效果動畫

  • friction 摩擦係數,預設40
  • tension 張力系數,預設7
  • bounciness
  • speed

Spring支援 friction與tension 或者 bounciness與speed 兩種組合模式,這兩種模式不能並存。 其中friction與tension模型來源於origami,一款F家自制的動畫原型設計工具,而bounciness與speed則是傳統的彈簧模型引數。

2、timing 帶有時間的漸變動畫

  • duration 動畫執行時間
  • easing 動畫曲線函式,可以從Easing模組中獲取更多預定義的函式
  • delay: 動畫執行延遲時間(單位:毫秒).預設為0ms

3、decay 帶有加速度值的動畫,類似於正弦值

  • velocity:初始速度,必須要填寫
  • deceleration:速度減小的比例,加速度。預設為0.997

4、另外,現在RN只為我們提供Animated.View,Animated.Image,Animated.Text三種動畫元件,但是可以通過Animated.createAnimatedComponent(component)建立動畫元件。

有了上面的知識,我們就可以簡單實現一個淡入的動畫效果:
這裡寫圖片描述

具體實現步驟如下:

1、引入Animated元件

import {
    StyleSheet,
    Animated,//要實現Animated 動畫,首先要引入Animated元件
Easing, View, Image, Text, }from 'react-native';

2、 在構造方法中初始化一個Animated物件

// 構造
    constructor(props) {
        super(props);
        // 初始狀態
        this.state = {
            anim: new Animated.Value(0),//初始化一個動畫物件
        };
    }

3、新增動畫元件,並將要改變的動畫元件樣式屬性值設為動畫物件

render() {
        return (
            <View style={styles.container}>
                <Animated.Image source={require('../../res/girl.jpg')}
                 style={[styles.image,{
                 opacity:this.state.anim,//將動畫物件賦值給需要改變的樣式屬性
                 }]}/>
            </View>
        )
    }

4、執行動畫

componentDidMount() {
         //timing動畫
        Animated.timing(//使用timin過度動畫
            this.state.anim,//要改變的動畫物件
            {
                toValue: 1,//動畫結束值
                duration: 3000,//動畫執行時間
                easing: Easing.linear,//動畫過渡曲線函式
            }
        ).start();//動畫開始執行

隨著動畫的執行,樣式屬性的值會一直變化,即實現動畫效果。

二、插值函式

我們可以只初始化一個動畫物件,然後給多個樣式屬性賦值,通過interpolate()方法,我們可以將一個區間對映到另一個區間。

render() {
        return (
            <View style={styles.container}>
                <Animted.Image source={require('../../res/girl.jpg')}
                               style={[styles.image,{
                               opacity:this.state.anim,//元件的opacity樣式屬性
                               transform:[//元件的transform樣式屬性
                               {scale:this.state.anim.interpolate({//元件的scale樣式屬性,將[0,1]區間對映到[1,2]區間
                               inputRange:[0,1],//輸入區間
                               outputRange:[1,2]//輸出區間
                               })},
                               {rotate:this.state.anim.interpolate({//元件的rotate樣式屬性,將[0,1]區間對映到['0deg','360deg]區間
                               inputRange:[0,1],//輸入區間
                               outputRange:['0deg','360deg'],//輸出區間
                               })},
                               ]
                               }]}
                />
            </View>
        )
    }

其它程式碼不變,則隨著動畫的執行,圖片的opacity,scale,rotate都會更改,產生一個淡入,放大,旋轉同時進行的動畫效果:
這裡寫圖片描述

三、組合動畫

RN為我們提供了三種組合動畫方法:

  • sequence 順序執行
  • parallel 同時執行
  • stagger 每隔一段時間開始執行行一個動畫

例如一個彈出字幕的效果:
這裡寫圖片描述

程式碼實現如下:

/**
 * Created by gyg on 2017/5/19.
 * Animated學習demo
 */
'use strict'
import React, {Component} from 'react';
import {
    StyleSheet,
    Animated,
    Easing,
    View,
    Image,
    Text,
    Dimensions,
}from 'react-native';
var deviceWidth=Dimensions.get('window').width;
export default class AnimatedDemo extends Component {

    // 構造
    constructor(props) {
        super(props);
        // 初始狀態
        this.state = {
            data: ['工行大公司都會', '好噶或更多更好的嘎', '規劃i哈哈開啟建行', '黑啊好的是根據卡號開個會看到個', '嘎機會更多機會關機定時關機'],
            initAnims:[],//存放初始化的動畫物件
            anims: [],//存放動畫函式
        };
        for(let i=0;i<this.state.data.length;i++){//遍歷資料
            this.state.initAnims.push(new Animated.Value(-250));//每條資料對應一個動畫物件
            this.state.anims.push(Animated.timing(this.state.initAnims[i],{//每條資料對應一個動畫函式
                duration:3500,
                toValue:deviceWidth+250,
                easing:Easing.linear,
            }));
        }
    }

    componentDidMount() {
        Animated.stagger(1000,this.state.anims).start();//每隔1000ms開始執行動畫陣列中的一個動畫
    }

    render() {
        const views=this.state.data.map((text,i)=>{//每條資料對映一個動畫元件
            return(
                <Animated.Text key={i}
                    style={[styles.textview,{
                    transform:[{
                    translateX:this.state.initAnims[i],//設定translateX為動畫值,這樣動畫執行的時候,translateX的值也會隨之改變
                    },
                    {
                    translateY:Math.floor(Math.random()*10),//產生0-9的一個隨機數
                    },
                    ]}]}
                >
                    {text}
                </Animated.Text>
            )
        });
        return (
            <View style={styles.container}>
                {views}
            </View>
        )
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: 'white',
        justifyContent: 'center',
    },
    textview:{
        width:200,
        paddingTop:10,
        paddingBottom:10,
        paddingLeft:20,
        paddingRight:20,
        backgroundColor:'rgba(0,0,0,0.5)',
        borderRadius:10,
        justifyContent:'center',
        alignItems:'center',
        fontSize:16,
        color:'white',
    },
});