React Native填坑之旅--動畫篇

React Native填坑之旅–Button篇
React Native填坑之旅–動畫
React Native填坑之旅–HTTP請求篇

動畫是提高用戶體驗不可缺少的一個元素。恰如其分的動畫可以讓用戶更明確的感知當前的操作是什麼。

無疑在使用React Native開發應用的時候也需要動畫。這就需要知道RN都給我們提供了那些動畫,和每個動畫可以處理的功能有哪些。

填坑材料Animated

動畫API提供了一些現成的組件:Animated.ViewAnimated.TextAnimated.Image默認支持動畫。動畫API會調用iOS或者Android的本地代碼來完成這些組件的位移、大小等動畫。這樣各種動畫在視覺上可以非常的流暢。

一個繞着屏幕轉的動畫

繞着屏幕轉的動畫:

<--< 
|  | 
V--^ 

基本代碼

export default class AnimatedSquare extends React.Component {
    constructor(props) {
        super(props);
        // 1
        this.state = {
            pan: new Animated.ValueXY
        }
    }

    getStyle() {
        return [
            styles.square,
            {
                transform: this.state.pan.getTranslateTransform()
            }
        ];
    }

    render() {
        return (
            <View style={styles.container}>
                <Animated.View style={this.getStyle()} />
            </View>
        );
    }
}

解釋一下:
1. 在this.state裏用了Animated.ValueXY的實例。這樣Animated動畫就知道需要處理的是X和Y兩個方向的值。
2. getStyle()方法會返回一個數組,因爲動畫需要squaretransform兩個樣式值。getTranslateTransform()方法會獲得一個數組:[{translateX: xValue}, {translateY: yValue}]xValueyValue就是根據我們開始的時候設置的Animated.ValueXY解析出來的。
3. 最後設置Animated.View。也就是動畫最終作用的元素(或者叫做視圖)。

依賴和樣式

依賴項:

import React from 'react';
import {
    Dimensions,
    StyleSheet,
    View,
    Animated
} from 'react-native';

let {
    width,
    height
} = Dimensions.get('window');

const SQUARE_DIMENSIONS = 30;

樣式:

const styles = StyleSheet.create({
    container: {
        flex: 1
    },
    square: {
        width: SQUARE_DIMENSIONS,
        height: SQUARE_DIMENSIONS,
        backgroundColor: 'blue'
    }
});

動起來

我們準備讓這個目標物體,一個方框,從左上角:x = 0, y = 0到左下角: x = 0, y = (screenHeight - squreHeight)

const SPRING_CONFIG = {tension: 2, friction: 3};

    componentDidMount() {
        Animated.spring(this.state.pan, {
            ...SPRING_CONFIG,
            toValue: {x: 0, y: height - SQUARE_DIMENSIONS}                        // return to start
        }).start();
    }

componentDidMount方法是RN組件的生命週期方法,在組件完成html渲染的時候調用。在組件渲染完成後開始動畫。

我們指定了SPRING_CONFIG爲彈簧動畫的彈力和摩擦力,值都不大。所以這個方框會在屏幕的每個角稍微彈幾下。

依次的動動動。。。

我們可以讓幾個動畫按照順序依次執行。這些動畫會一個挨一個的執行。sequence方法是動畫API組織動畫的多種方式之一。也可以使用parallel來組織,這樣幾個動畫會並行執行。

    componentDidMount() {
        Animated.sequence([
            Animated.spring(this.state.pan, {
                ...SPRING_CONFIG,
                toValue: {x: 0, y: height - SQUARE_DIMENSIONS} //animate to bottom left
            }),
            Animated.spring(this.state.pan, {
              ...SPRING_CONFIG,
              toValue: {x: width - SQUARE_DIMENSIONS, y: height - SQUARE_DIMENSIONS} // animated to bottom right
            }),
            Animated.spring(this.state.pan, {
                ...SPRING_CONFIG,
                toValue: {x: width - SQUARE_DIMENSIONS, y: 0} //animate to top right
            }),
            Animated.spring(this.state.pan, {
              ...SPRING_CONFIG,
              toValue: {x: 0, y: 0} // return to start
            })
        ]).start(cb);
    }

我們定義了四個彈簧動畫。這個四個一組的動畫執行的順序就是前文指定的順序。

重複動畫

調用動畫的start方法的時候可以傳入一個回調方法作爲參數。這個回調方法會在這一組動畫執行完成之後調用。在本例中,每次動畫執行完之後會再次調用開始動畫的方法。

    componentDidMount() {
        this.startAndRepeat();
    }

    startAndRepeat() {
        this.triggerAnimation(this.startAndRepeat);
    }

    triggerAnimation(cb) {
        Animated.sequence([
            Animated.spring(this.state.pan, {
                ...SPRING_CONFIG,
                toValue: {x: 0, y: height - SQUARE_DIMENSIONS} //animate to bottom left
            }),
            Animated.spring(this.state.pan, {
              ...SPRING_CONFIG,
              toValue: {x: width - SQUARE_DIMENSIONS, y: height - SQUARE_DIMENSIONS} // animated to bottom right
            }),
            Animated.spring(this.state.pan, {
                ...SPRING_CONFIG,
                toValue: {x: width - SQUARE_DIMENSIONS, y: 0} //animate to top right
            }),
            Animated.spring(this.state.pan, {
              ...SPRING_CONFIG,
              toValue: {x: 0, y: 0} // return to start
            })
        ]).start(cb);
    }

調用triggerAnimation方法開始動畫,並傳入再次開始動畫的方法startAndRepeat

完整代碼

import React from 'react';
import {
    Dimensions,
    StyleSheet,
    View,
    Animated
} from 'react-native';

let {
    width,
    height
} = Dimensions.get('window');

const SQUARE_DIMENSIONS = 30;
const SPRING_CONFIG = {tension: 2, friction: 3};

export default class AnimatedSquare extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            pan: new Animated.ValueXY
        }

        // bind
        this.startAndRepeat = this.startAndRepeat.bind(this);
        this.getStyle = this.getStyle.bind(this);
        this.startAndRepeat = this.startAndRepeat.bind(this);
        this.triggerAnimation = this.triggerAnimation.bind(this);
    }

    componentDidMount() {
        this.startAndRepeat();
    }

    startAndRepeat() {
        this.triggerAnimation(this.startAndRepeat);
    }

    getStyle() {
        return [
            styles.square,
            {
                transform: this.state.pan.getTranslateTransform()
            }
        ];
    }

    triggerAnimation(cb) {
        Animated.sequence([
            Animated.spring(this.state.pan, {
                ...SPRING_CONFIG,
                toValue: {x: 0, y: height - SQUARE_DIMENSIONS} //animate to bottom left
            }),
            Animated.spring(this.state.pan, {
              ...SPRING_CONFIG,
              toValue: {x: width - SQUARE_DIMENSIONS, y: height - SQUARE_DIMENSIONS} // animated to bottom right
            }),
            Animated.spring(this.state.pan, {
                ...SPRING_CONFIG,
                toValue: {x: width - SQUARE_DIMENSIONS, y: 0} //animate to top right
            }),
            Animated.spring(this.state.pan, {
              ...SPRING_CONFIG,
              toValue: {x: 0, y: 0} // return to start
            })
        ]).start(cb);
    }

    render() {
        return (
            <View style={styles.container}>
                <Animated.View style={this.getStyle()} />
            </View>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1
    },
    square: {
        width: SQUARE_DIMENSIONS,
        height: SQUARE_DIMENSIONS,
        backgroundColor: 'blue'
    }
});

更多相關代碼請移步:https://github.com/future-challenger/petshop/tree/master/client/petshop

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章