React Native填坑之旅–Button篇
React Native填坑之旅–動畫
React Native填坑之旅–HTTP請求篇
動畫是提高用戶體驗不可缺少的一個元素。恰如其分的動畫可以讓用戶更明確的感知當前的操作是什麼。
無疑在使用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://github.com/future-challenger/petshop/tree/master/client/petshop