【譯】React Native 動畫 API 入門實例

翻譯自 React-native Animated API Basic Example
翻譯過程當中有刪改javascript


簡介

本文是探索 react-native 中實現的的 Animated API,Web 版本上的 React 沒有該 API,不過可使用在 react-europe 大會上發佈的 react-motion
本文中將會完成一個動畫例子,效果以下圖
JlX4nV0.gifhtml

原理

Animated API的原理並不是經過 setState 方法使 react 重渲染,而是使用 setNativeProps 方法更新 native 視圖。
Animated API 導出了幾個特殊的 components:Animated.View, Animated.Text, 和 Animated.Image。Animated API 直接在 Objective-C 的 native 環境中調整這些 components 的外觀樣式,跳過了 JS 環境中 react 的 diff 與 reconciliation 過程,從而得到流暢、高效的動畫。
簡而言之,它將對動畫中變化的屬性數值作插值運算而且刷新 native 視圖。java

動畫效果:沿屏幕移動的方塊

咱們將實現一個簡單的動畫效果:沿手機屏幕四個邊,按照左上角 -> 左下角 -> 右下角 -> 右上角的順序,移動一個正方形。示意圖大概以下react

< -- <           
|    |          
V -- ^

開始

導入依賴

import React, {  
    AppRegistry,
    Component, 
    Dimensions,  
    StyleSheet,  
    View,  
    Animated 
} from 'react-native';
const { 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'  
    } 
});

基本邏輯

class AnimatedSquare extends Component {
    constructor(props) {
        super(props);

        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>
        );
    }
}

上面代碼中有幾個須要解釋的地方。
注意咱們所創建的 componentstateAnimated.ValueXY 的一個實例。這個 API 將在 XY 兩個值上進行插值。git

getStyle() 方法,返回一個樣式對象數組。包括描述了方塊寬高大小的 square 基本樣式,以及最爲重要的,一個 transform 樣式對象。
咱們使用 getTranslateTransform 這個 Animated API 中的 helper 方法,來返回一個適合 transform 屬性結構的值。
這個返回值的結構相似於[{ translateX: xValue}, {translateY: yValue}],xValue 和 yValue 是計算後的插值。github

最後咱們使用 Animated.View,表示這個組件是可動畫組件。spring

移動正方形

一開始正方形是靜止在左上角的,如今咱們把它從左上角(x = 0, y = 0)移動到左下角(x = 0, y = (屏幕高 - 正方形高)react-native

const SPRING_CONFIG = {tension: 2, friction: 3}; //Soft spring
//...
    componentDidMount() {
        Animated.spring(this.state.pan, {
          ...SPRING_CONFIG,
          toValue: {x: 0, y: height - SQUARE_DIMENSIONS}  // return to start
        }).start();
    }

在組件裝載後,咱們經過 Animated.spring 進行 Spring(彈性)動畫 ,咱們給彈性動畫設置了 SPRING_CONFIG 配置,包括 tension(張力)和 friction(摩擦)值,因此正方形到達左下角後,會有一個小小回彈動畫。api

再動,又動,還動

咱們會創建一個順序的動畫序列,讓動畫一個接一個進行。固然除了 sequence(順序),你還能夠按 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();
}

如以前設想的同樣,咱們定義了4個彈性動畫。註釋解釋了動畫移動方向。

一直不停動

Animated.sequence(animtionList: Arrary).start(cb: Function);

動畫序列的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);
 }

咱們把動畫邏輯提取爲一個方法,在完成回調函數中觸發它。

所有代碼

import React, {  
    AppRegistry,
    Component, 
    Dimensions,  
    StyleSheet,  
    View,  
    Animated 
} from 'react-native';
const { width,  height } = Dimensions.get('window');
const SQUARE_DIMENSIONS = 30;

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


class AnimatedSquare extends Component {
    constructor(props) {
        super(props);

        this.state = {
            pan: new Animated.ValueXY()
        }
    }
    
    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);
    }
     
    getStyle() {
        return [styles.square, {
            transform: this.state.pan.getTranslateTransform()
        }];
    }
    
    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'  
    } 
});

AppRegistry.registerComponent('AnimatedSquare', () => AnimatedSquare);

其它一些範例

react-native-animated-demo-tinder
UIExplorer Animated example

相關資源

Cheng Lou – The State of Animation in React at react-europe 2015
react-motion – Github
React Native Animation API
Spencer Ahrens – React Native: Building Fluid User Experiences at react-europe 2015

相關文章
相關標籤/搜索