React Native填坑之旅--Button篇
React Native填坑之旅--動畫
React Native填坑之旅--HTTP請求篇
動畫是提高用戶體驗(yàn)不可缺少的一個(gè)元素淌友。恰如其分的動畫可以讓用戶更明確的感知當(dāng)前的操作是什么。
無疑在使用React Native開發(fā)應(yīng)用的時(shí)候也需要?jiǎng)赢嬐驴_@就需要知道RN都給我們提供了那些動畫,和每個(gè)動畫可以處理的功能有哪些播聪。
填坑材料Animated
動畫API提供了一些現(xiàn)成的組件:Animated.View
癣猾,Animated.Text
和Animated.Image
默認(rèn)支持動畫慕蔚。動畫API會調(diào)用iOS或者Android的本地代碼來完成這些組件的位移、大小等動畫巍佑。這樣各種動畫在視覺上可以非常的流暢倒源。
一個(gè)繞著屏幕轉(zhuǎn)的動畫
繞著屏幕轉(zhuǎn)的動畫:
<--<
| |
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>
);
}
}
解釋一下:
- 在
this.state
里用了Animated.ValueXY
的實(shí)例。這樣Animated
動畫就知道需要處理的是X和Y兩個(gè)方向的值句狼。 -
getStyle()
方法會返回一個(gè)數(shù)組笋熬,因?yàn)閯赢嬓枰?code>square和transform
兩個(gè)樣式值。getTranslateTransform()
方法會獲得一個(gè)數(shù)組:[{translateX: xValue}, {translateY: yValue}]
腻菇。xValue
和yValue
就是根據(jù)我們開始的時(shí)候設(shè)置的Animated.ValueXY
解析出來的胳螟。 - 最后設(shè)置
Animated.View
。也就是動畫最終作用的元素(或者叫做視圖)筹吐。
依賴和樣式
依賴項(xiàng):
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'
}
});
動起來
我們準(zhǔn)備讓這個(gè)目標(biāo)物體糖耸,一個(gè)方框,從左上角: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渲染的時(shí)候調(diào)用。在組件渲染完成后開始動畫洋侨。
我們指定了SPRING_CONFIG
為彈簧動畫的彈力和摩擦力舍扰,值都不大。所以這個(gè)方框會在屏幕的每個(gè)角稍微彈幾下希坚。
依次的動動動边苹。。裁僧。
我們可以讓幾個(gè)動畫按照順序依次執(zhí)行个束。這些動畫會一個(gè)挨一個(gè)的執(zhí)行慕购。sequence
方法是動畫API組織動畫的多種方式之一。也可以使用parallel
來組織茬底,這樣幾個(gè)動畫會并行執(zhí)行沪悲。
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);
}
我們定義了四個(gè)彈簧動畫。這個(gè)四個(gè)一組的動畫執(zhí)行的順序就是前文指定的順序阱表。
重復(fù)動畫
調(diào)用動畫的start
方法的時(shí)候可以傳入一個(gè)回調(diào)方法作為參數(shù)殿如。這個(gè)回調(diào)方法會在這一組動畫執(zhí)行完成之后調(diào)用。在本例中捶枢,每次動畫執(zhí)行完之后會再次調(diào)用開始動畫的方法。
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);
}
調(diào)用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'
}
});
更多相關(guān)代碼請移步:https://github.com/future-challenger/petshop/tree/master/client/petshop