React Native 中實(shí)現(xiàn)動(dòng)畫(huà)的主要框架就是 Animated
, 經(jīng)過(guò)很多版本的迭代嗤军,Animated 已經(jīng)相當(dāng)完善,基本能滿(mǎn)足日常開(kāi)發(fā)需求了晃危。
關(guān)于 Animated 的資料叙赚,文檔網(wǎng)上有很多了,這里不展開(kāi)來(lái)詳細(xì)講僚饭【兰螅總結(jié)一下自己開(kāi)發(fā)這段時(shí)間來(lái)對(duì) Animated 的小理解。
Animated VS 原生動(dòng)畫(huà)
蘋(píng)果對(duì)自家平臺(tái) App 的用戶(hù)體驗(yàn)非常重視浪慌,可以說(shuō)現(xiàn)在 RN 平臺(tái)的動(dòng)畫(huà)庫(kù),完全比不上 iOS SDK 所能提供的效果朴则,充其量只能算是一個(gè)精華簡(jiǎn)化版本权纤。而且蘋(píng)果每年的 WWDC 對(duì)自家動(dòng)畫(huà)庫(kù)的更新都十分重視。
在一些 Animated 組件中可以打開(kāi) useNative
開(kāi)關(guān)乌妒,從而借助 iOS 平臺(tái)底層的動(dòng)畫(huà)庫(kù)實(shí)現(xiàn)效果汹想,以得到更加流暢的效果。
相比于原生動(dòng)畫(huà)的實(shí)現(xiàn)方式撤蚊,Animated:
- 目前只支持
Animated.Text
古掏,Animated.Image
,Animated.View
等幾個(gè)指定的組件侦啸,自定義的動(dòng)畫(huà)組件實(shí)現(xiàn)比較麻煩槽唾。 - 必須使用
Animated Value
來(lái)作為動(dòng)畫(huà)的驅(qū)動(dòng),而不是被動(dòng)畫(huà)組件的具體狀態(tài)光涂,不直觀庞萍。 - 整個(gè)動(dòng)畫(huà)的代碼被割裂,導(dǎo)致回溯閱讀修改麻煩忘闻。
Animated VS LayoutAnimated
LayoutAnimated
是基于組件 Frame 的簡(jiǎn)化版數(shù)據(jù)庫(kù)钝计,非常類(lèi)似于 Cocoa 中的 UIView animate block。而 Animated 是基于 Value 驅(qū)動(dòng)的齐佳,要復(fù)雜的多私恬。
在一些場(chǎng)景下,LayoutAnimated 要比 Animated 更為直接簡(jiǎn)單炼吴。
LayoutAnimated 簡(jiǎn)單實(shí)現(xiàn):
startAnimation() {
LayoutAnimation.configureNext({
duration: 700, //持續(xù)時(shí)間
create: { // 視圖創(chuàng)建
type: LayoutAnimation.Types.spring,
property: LayoutAnimation.Properties.scaleXY,// opacity本鸣、scaleXY
},
update: { // 視圖更新
type: LayoutAnimation.Types.spring,
},
});
this.setState({width: this.state.width + 10, height: this.state.height + 10});
}
Animated 核心邏輯
總結(jié)為兩個(gè)詞:綁定和驅(qū)動(dòng)。
綁定缺厉,就是將組件的屬性和 Animated.Value 綁定永高,value 值變化驅(qū)動(dòng)組件重繪形成動(dòng)畫(huà)隧土。這和原生動(dòng)畫(huà)中使用組件本身的屬性來(lái)驅(qū)動(dòng)動(dòng)畫(huà)是有區(qū)分的。
驅(qū)動(dòng)命爬,就是控制 Animated.Value 的值在一段時(shí)間內(nèi)的函數(shù)變化曹傀,可以使用現(xiàn)成的曲線(etc: Easing),也可以綁定到其它 value 值上形成映射(Animated.Event)饲宛,Animated 的很多功能皆愉,本質(zhì)上就是對(duì)于 Animated.Value 的不同驅(qū)動(dòng)方式。
也貼一點(diǎn)代碼艇抠,一個(gè)簡(jiǎn)單的 Animated 實(shí)現(xiàn):
constructor(props) {
super(props);
this.state = {
// 定義一個(gè) Animated.Value 值
bgOpacity: new Animated.Value(0),
};
this.bgOpacityToValue = 1;
}
componentDidMount() {
let animates = [
// 使用 Animated 庫(kù)函數(shù)驅(qū)動(dòng)這個(gè) Value 值變化幕庐,形成動(dòng)畫(huà)
Animated.spring(this.state.bgOpacity, {
toValue: this.bgOpacityToValue,
})
];
this.state.bgOpacity.setValue(0);
Animated.parallel(animates).start();
}
render() {
return (
<View style={styles.screen} pointerEvents='box-none'>
// 定義 Animated.View, 并且將 Animated.Value 綁定到它的一個(gè)屬性上
<Animated.View
style={[styles.screen, styles.screenFlexStyle, {backgroundColor: 'rgba(0, 0, 0, 0.3)', opacity: this.state.bgOpacity}]}>
</View>
</Animated.View>
</View>
);
}
其它 Animated 的高級(jí)實(shí)現(xiàn),比如映射家淤、綁定异剥、組合、關(guān)鍵幀絮重、動(dòng)畫(huà)曲線等等冤寿,可以參考文檔。
Animated Event
起初對(duì) Animated.Event
綁定非常疑惑青伤,也算是 Animated 中比較難理解的一個(gè)點(diǎn)督怜,官方的示例代碼如下:
onScroll={Animated.event(
[{nativeEvent: {contentOffset: {x: this._scrollX}}}],
{listener: (event) => console.log(event)}, // Optional async listener
)}
文檔本身對(duì)此沒(méi)有太多解釋。這一段中狠角,onScroll 是配置 ScrollView 的回調(diào)函數(shù)号杠,所以 Animated.event() 其實(shí)返回的是一個(gè)方法,它將會(huì)被 scroll 調(diào)用丰歌,傳入當(dāng)前的滾動(dòng)事件 e姨蟋。
Animated.event() 的第一個(gè)參數(shù)是綁定序列:規(guī)定了如何將 Animated.Value() 綁定到指定的源上,可以看做是一種特殊的語(yǔ)法立帖,用偽代碼寫(xiě)就是:
e.nativeEvent.contentOffset.x 綁定到 this._scrollX
第二個(gè)參數(shù)是 config芬探,這里指定了事件的監(jiān)聽(tīng)函數(shù),其實(shí)就是 onScroll 的正常監(jiān)聽(tīng)函數(shù)厘惦,其它可選的配置見(jiàn)文檔偷仿。
所以可以翻譯一下這段拗口的語(yǔ)法,等于:
onScroll={(e) => {
Animated.event(
[{nativeEvent: {contentOffset: {x: this._scrollX}}}],
{listener: (event) => console.log(event)}, // Optional async listener
)(e);
}}