React Native填坑之旅--動畫篇

CecileChast 8年前發布 | 5K 次閱讀 ReactNative 移動開發 React Native

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

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

填坑材料Animated

動畫API提供了一些現成的組件: Animated.View , Animated.Text 和 Animated.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() 方法會返回一個數組,因為動畫需要 square 和 transform 兩個樣式值。 getTranslateTransform() 方法會獲得一個數組: [{translateX: xValue}, {translateY: yValue}] 。 xValue 和 yValue 就是根據我們開始的時候設置的 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://segmentfault.com/a/1190000007024556

 

 本文由用戶 CecileChast 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!