[React Native] 動畫 · Animated
如果要在一個 React Native 應用中添加動畫效果,首先考慮的就是官方提供的 Animated 。通過定義輸入和輸出的動畫狀態值,調用 start/stop 方法就可以執行起來,使用上可以說是非常友好方便。
Animated 動畫組件有:
如果讓某個界面元素擁有動畫效果,那它應該使用如下的動畫組件:
- Animated.View
- Animated.Text
- Animated.Image
- Animated.ScrollView
- Animated.FlatList
- Animated.SectionList
動畫類型:
Animated 可以創建三種動畫類型,直接調用就能創建一個動畫對象,如 Animated.timing(…) ,動畫對象可以調用 start() 啓動。
- timing 先加速至全速,再逐漸減速漸停 (使用最多)
- spring 執行動畫時會超出最終值然後彈回,適用於彈性的動態效果
- decay 以一定的初始速度開始變化,然後變化速度越來越慢直至停下
例如:
// 創建並啓動一個 spring 類型的動畫
Aimated.spring(this.state.value,{...動畫配置}).start();
動畫值:
動畫執行過程中的變量不是普通的變量,只能是動畫值,有兩種類型:
- Animated.Value() 表示單個值
- Animated.ValueXY() 表示向量(矢量)值. (可用於改變 x,y 向的座標)
實例:
this.state={
dynamicHeight:new Animated.Value(0) //初始化
}
this.state.dynamicHeight.setValue(10); //重新設定動畫值
計算動畫值
一些時候所用的動畫值並不是可以直接設定的,或者多個動畫值存在一定關係,轉換後使用更加方便,也就是在使用之前需要進行計算,這時就需要用到一些常用計算方法。
例如: 一個長寬比爲 3:2 的圖片,要實現放大動畫,爲保持變化過程中的長寬比不變,這時就可以用一個公因數, 則 寬度 = 公因數 乘以 2 ,長度 = 公因數 乘以 3 ,在 state 中變化的動畫值就是這個公因數,實際使用的動畫值就是通過乘法 Animated.multiply() 得到的動畫值。
// - 寬度計算 -
// factor 是 state 中的變量 ,乘以 2 得到真實動畫值後再將真實動畫值設置到屬性或樣式上
const realAnimatedWidth = Animated.multiply(this.state.factor,new Animated.Value(2));
可用的計算方法:
- Animated.add() 加
- Animated.subtract() 減
- Animated.multiply() 乘
- Animated.divide() 除
- Animated.modulo() 模(取餘)
組合類型
當多個動畫需要需要以一定規律進行(多個動畫值變動)的時候,就需要用到組合動畫,Animated 提供了幾種方法將動畫按照一定方式組合:
- Animated.delay(time) 按照給定延遲後執行動畫
- Animated.parallel(animations, config?) 同時執行多個動畫
- Animated.sequence(animations) 順序執行,一個動畫結束後開始執行下一個
- Animated.stagger(time, animations) 按照給定延遲順序啓動多個動畫,可能並行重疊。
- loop(animation, config?) 無限循環一個指定動畫
舉例:
//並行兩個動畫 同時進行一個 timing 動畫和一個 spring 動畫
Animated.parallel([
Animated.timing(...),
Animated.spring(...)
]).start()
插值
根據一個動畫值的變化範圍,獲取另一個動畫值的範圍,需要使用插值。 插值函數 interpolate 可以將輸入範圍映射到輸出範圍,一般是使用線性插值,但也可以指定 easing 函數 [查看 Easing 介紹]。
對於一些屬性值是字符串的變量,比如 rotate ,從 ‘0deg’ 到 ‘180deg’ ,也應該使用插值來獲取變化範圍。
- AnimatdValue.interpolate(config)
例如:
// Y 軸上的偏移量根據 fadeAnim 的值變化
// fadeAnim -> [0,1] translateY -> [150,0]
style={{
opacity: this.state.fadeAnim, // Binds directly
transform: [{
translateY: this.state.fadeAnim.interpolate({
inputRange: [0, 1],
outputRange: [150, 0] // 0 : 150, 0.5 : 75, 1 : 0
}),
}],
}}
一個完整的簡單動畫可以分如下幾步實現:
-
定義動畫值變量並初始化
-
定義動畫配置和執行方法
-
將動畫值綁定到組件上,用於改變屬性或樣式
-
觸發動畫執行的事件或操作
對應於下面的例子來看,會更容易理解上述每一步。
[ 動畫1 -動畫值控制高度] 點擊展開視圖的動畫,效果如下:
通過將 Animated.View 的高度設置成動畫值實現。 以上三步均在代碼中標出
export default class AnimatedView extends PureComponent {
constructor(props) {
super(props);
this.state = {
dynamicHeight: new Animated.Value(0), //1 - 構造方法中定義 dynamicHeight 動畫變量
}
}
startViewAnimation = () => { //2 - 配置動畫和定義執行方法
let targetHeight = 80;
if (this.state.dynamicHeight._value === 80) { //根據高度判斷是否已展開
targetHeight = 0; //已展開的話,則需要將 View 收起,高度回到初始值 0
}
Animated.spring( //定義彈性動畫
this.state.dynamicHeight, //改變的動畫變量
{
toValue: targetHeight, //目標值,也就是動畫值變化後的最終值
duration: 300, //動畫持續時長
}
).start();
}
render() {
return (
<View style={{flex: 1}}>
<View style={{width: 200, position: 'absolute', top: 30}}>
<View>
<TouchableOpacity
onPress={this.startViewAnimation} // 4 - 觸發動畫執行的點擊事件
style={styles.button}>
<Text>點擊展開</Text>
</TouchableOpacity>
</View>
<Animated.View
style={{backgroundColor: '#eee', justifyContent: 'space-around',
height: this.state.dynamicHeight}} //3 - 將高度是設爲動畫值,即用動畫值改變 style (height)
>
</Animated.View>
</View>
</View>
);
}
}
[ 動畫2 - 動畫值插值轉換] 點擊跳動的心形圖:
動畫值 imageSize 從 0 到 1,映射成對應的實際圖片大小,再設置到 Animated.Image 的寬高樣式上。
export default class HeartView extends PureComponent {
constructor(props) {
super(props);
this.state = {
imageSize: new Animated.Value(0), //動畫初始值爲 0,目標值爲 1 。會經過插值轉換後應用到 Animated.Image上
}
}
startImageAnimation = () => {
//動畫開始前先將初始值設爲0,否則第一次執行後this.state.imageSize的動畫值一直是 1, 不再有效果
this.state.imageSize.setValue(0)
Animated.spring(
this.state.imageSize,
{
toValue: 1,
duration: 2000,
}
).start();
}
render() {
//動畫值從0-1,對其進行插值轉換 ,映射成實際的圖片大小
//再將實際的圖片大小設置給 Animated.Image 的樣式
const imageSize = this.state.imageSize.interpolate({
inputRange: [0, 0.5, 1],
outputRange: [30, 40, 30]
});
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<TouchableOpacity
onPress={this.startImageAnimation}
>
<Animated.Image source={ICON_HEART}
style={{width: imageSize, height: imageSize, //應用到 Animated.Image 上
tintColor: '#dc3132'}}/>
</TouchableOpacity>
</View>
);
}
}
這些都是動畫 Animated 的基礎使用,另外還有關於 setNativeProps 和 LayoutAnimation 的部分另做總結。
歡迎關注公衆號 Export ,發現更多內容。