React native 動(dòng)畫(huà)詳解

參考文檔 :動(dòng)畫(huà)概述鼎兽、動(dòng)畫(huà)API答姥、動(dòng)畫(huà)緩動(dòng)函數(shù)interactionmanager

文檔其實(shí)已經(jīng)比較詳細(xì)了谚咬,這里整理一下鹦付,做個(gè)筆記,也算加深一下自己的印象择卦。

定義

所謂動(dòng)畫(huà)敲长,就是 UI 上的變化,而在 RN 中秉继,UI 對(duì)應(yīng)的就是 style祈噪,那么動(dòng)畫(huà)就是要順滑的改變 style 某個(gè)屬性的值,就以 opacity 來(lái)作為解釋吧

如果想要實(shí)現(xiàn)透明度的不斷變化尚辑,比如可以用 state 中定義的屬性作為 style.opacity 的值辑鲤,后續(xù)通過(guò) requestAnimationFrame 來(lái)不停修改 state 值,可以做到杠茬,但性能低下月褥,這就是 RN Animated 存在的意義

const opacity = new Animated.Value(0);
<Animated.View  style={{
  opacity: opacity
}}/>

這就好了,opacity 的屬性值為一個(gè) Animated.Value瓢喉,當(dāng)需要修改透明度的了宁赤,不需要改 state, 而是重置
Animated.Value 的值就行了栓票,他會(huì)實(shí)時(shí)傳導(dǎo)給 View 的 style.opacity

動(dòng)起來(lái)

如何修改 Animated.Value 的值

// 定義 opacity 的值如何變化
const an = Animated.timing(opacity,  {
    toValue: 1,  // 最終變?yōu)?1
    delay: 1000,  // 延遲 1 秒后開(kāi)始變換(在此期間礁击,opacity 值仍為 0)
    duration: 1000,  // 用 1 秒的時(shí)間去變換(在 1 秒內(nèi),opacity 由 0 -> 1)
    easing: Easing.back(),  // 變換過(guò)程不一定是均勻變換逗载,可指明變換函數(shù)
    useNativeDriver:false, // 是否啟用原生驅(qū)動(dòng)
    isInteraction:true, // 是否在InteractionManager的隊(duì)列中注冊(cè)哆窿,后面會(huì)提到
})

// 開(kāi)始動(dòng)畫(huà) (可選:設(shè)置執(zhí)行完成的回調(diào))
an.start([callback]);

// 停下動(dòng)畫(huà)
an.stop()

說(shuō)下配置項(xiàng)中的三個(gè)參數(shù)

const a = new Animated.Value(0);
Animated.timing(opacity,  {
    toValue: a,
    useNativeDriver:false,
    isInteraction:true, 
})

toValue
可以指定為其他 Animated.Value 動(dòng)態(tài)值,而不是固定值

useNativeDriver
啟用原生使用ui線程厉斟,可提供動(dòng)畫(huà)流暢度挚躯,但有限制
1)啟用了原生,那么和該動(dòng)畫(huà)相關(guān)的其他動(dòng)畫(huà)也必須是原生
2)不支持用在 style 的盒模型屬性上擦秽,啟用原生驅(qū)動(dòng)码荔,最安全的就是 opacity 和 transform

isInteraction
RN 有一個(gè) interactionmanager API,可執(zhí)行一些耗時(shí)任務(wù)感挥,這些任務(wù)會(huì)在動(dòng)畫(huà)執(zhí)行完畢后再啟動(dòng)缩搅,通過(guò)該屬性來(lái)指明是否支持該 API, 默認(rèn)為 true触幼,即動(dòng)畫(huà)完事再執(zhí)行任務(wù)硼瓣,設(shè)為 false 則不然

除了 Animated.timing,還有兩個(gè)已經(jīng)預(yù)置了 easing 效果的方法:Animated.decayAnimated.spring

合成

假設(shè)我們要做一個(gè)堂鲤,由透明逐漸顯示亿傅、且寬度從 0 變到 100 的 view,透明度就是上面的辦法了瘟栖,那么寬度呢葵擎,我們可以重新定義一個(gè)

const width = new Animated.Value(0);
const w = Animated.timing(width,  {
    toValue: 100,  
    ...... // 其他配置,與 opacity 保持一致
})
w.start()

這樣搞半哟,配置就要寫(xiě)兩遍酬滤,當(dāng)然,也可以把配置提出來(lái)用一個(gè)變量來(lái)定義寓涨,這樣寫(xiě)一遍就好了敏晤,但這樣,要同時(shí)調(diào)用兩個(gè)動(dòng)畫(huà)的 start() 缅茉,兩個(gè)執(zhí)行可能不完全同步嘴脾,這就是合成的意義了,可以這樣寫(xiě)

const opacity = new Animated.Value(0);
const width = Animated.multiply(opacity, 100);
<View  style={{
  opacity: opacity,
  width:width
}}/>

Animated.timing(opacity,  {
    ...
}).start()

使用合成蔬墩,只需要啟動(dòng) opacity 就好了译打,width 的值會(huì)自動(dòng)跟著 opacity 變化,妙啊~

RN 提供了 Animated.add (加)拇颅、Animated.subtract (減)奏司、Animated.multiply(乘)、Animated.divide(除)樟插、Animated.modulo(取余) 五個(gè)合成方法

插值

上面的合成函數(shù)雖然方便韵洋,但實(shí)際操作中遠(yuǎn)不止加減乘除這么簡(jiǎn)單,那么能不能自定義合成函數(shù)呢黄锤?答案是不太能愚隧,因?yàn)樽远x函數(shù)意味著艾凯,動(dòng)畫(huà)跑起來(lái)之后,還要和 js 交互流强,來(lái)拿計(jì)算結(jié)果,在性能上是不能接受的打月,卡成幻燈片的動(dòng)畫(huà)還叫動(dòng)畫(huà)嗎队腐,也許未來(lái)某一天奏篙,RN 可以靜態(tài)編譯 js 無(wú)狀態(tài)純函數(shù)就可以了,那在那一天到之前,插值是當(dāng)前的選擇

const opacity = new Animated.Value(0);
const width = opacity.interpolate({
  inputRange: [0, 0.5, 1],
  outputRange: [0, 20, 100]
});
<View  style={{
opacity: opacity,
width:width
}}/>

Animated.timing(opacity,  {
    ...
}).start()

插值通過(guò) interpolate 將輸入 inputRange 映射為 outputRange悠就,可分段映射千绪,這樣靈活性大大提高

組合

產(chǎn)品改需求了充易,先逐漸顯示,然后再逐漸變寬盹靴。好簡(jiǎn)單稿静,在 opacitystart() 加回調(diào) -> 啟動(dòng) widthstart();可以改备,但如果有 N 個(gè)連續(xù)動(dòng)畫(huà),回調(diào)恐怕會(huì)吐血盐捷,這就需要組合動(dòng)畫(huà)了

Animated.sequence 按順序執(zhí)行一系列動(dòng)畫(huà)
Animated.delay 在順序執(zhí)行動(dòng)畫(huà)時(shí)默勾,可以讓兩個(gè)動(dòng)畫(huà)銜接時(shí)母剥,延遲一會(huì)

Animated.sequence([
  Animated.timing(opacity,  {
        ...
  })
  Animated.delay(1000);  // 有點(diǎn)像 sleep 
  Animated.timing(width,  {
        ...
  })
]).start()

假如希望 opacitywidth 同時(shí)執(zhí)行,但動(dòng)畫(huà)過(guò)程不是一個(gè)步調(diào)环疼,使用不同的 easing 或 duraition

Animated.parallel 同時(shí)執(zhí)行一系列動(dòng)畫(huà)
Animated.stagger 延遲啟動(dòng)一系列動(dòng)畫(huà)炫隶,基本等同于 setTimeout(Animated.parallel)

Animated.parallel([
  Animated.timing(opacity,  {
        ...
  })
  Animated.timing(width,  {
        ...
  })
]).start()

Animated.stagger(1000, [
  Animated.timing(opacity,  {
        ...
  })
  Animated.timing(width,  {
        ...
  })
]).start()

Animated.loop 循環(huán)執(zhí)行動(dòng)畫(huà)

可以在 start 回調(diào)中再啟動(dòng)自身,這樣也能搞一個(gè)循環(huán)動(dòng)畫(huà)爸吮,但這樣的話望门,每次結(jié)束筹误,是 ui 線程調(diào)用 js, 再喚起 ui,周而復(fù)始友存,不如直接 loop 性能來(lái)的好

Animated.loop(Animated.timing(opacity,  {
    ...
   isInteraction:false
}),  {
    iterations:-1
})
  1. 函數(shù)的第二個(gè)參數(shù)是設(shè)置循環(huán)次數(shù)的陶衅,可省略
  2. 建議配置 isInteraction=false 是因?yàn)檫@是循環(huán)動(dòng)畫(huà),會(huì)導(dǎo)致其他任務(wù)永遠(yuǎn)無(wú)法執(zhí)行了

交互

以上都是提前設(shè)計(jì)好動(dòng)畫(huà)膨俐,然后執(zhí)行罩句,在實(shí)際使用中動(dòng)畫(huà)门烂,其實(shí)就是 style 可能是需要根據(jù)用戶操作來(lái)進(jìn)行變動(dòng)的,比如這樣的組件 react-native-parallax-scroll-view蔓姚,這就需要用到 Animated.event 函數(shù)了

// argMapping 是映射配置
// config 接受兩個(gè)參數(shù), js 異步回調(diào)函數(shù) 和 是否啟用原生驅(qū)動(dòng)
Animated.event(argMapping, {
    listener:Function,
    useNativeDriver:true
})

需要先強(qiáng)調(diào)一件事氓润,否則看 argMapping 恐怕會(huì)有點(diǎn)懵

Animated 的本質(zhì)是提供輸入值,比如上面通過(guò) 初始value配置toValue 明確提供一個(gè)區(qū)間值作為輸入值挨措,亦或通過(guò) RN 提供的合成或插值將區(qū)間值進(jìn)行調(diào)整崩溪,說(shuō)白了,就是不希望 js 內(nèi)進(jìn)行任何計(jì)算觉既,要么提供明確的值乳幸,要么使用 RN 的計(jì)算手段粹断。

而交互呢,其實(shí)就是將 RN 提供的值瓶埋,再作為輸入值,還是不讓 js 參合曾撤,雖然提供了 listener 回調(diào)挤悉,但也只是讓你知道當(dāng)前執(zhí)行到哪了,是不能進(jìn)行修改的尖啡,并且這個(gè)回調(diào)的值可能還有延遲衅斩。argMapping 就是將 RN 提供的輸出值 映射 到一個(gè)動(dòng)畫(huà)上作為輸入值怠褐。

原生交互,也就是手勢(shì)交互奠涌,只有一個(gè) panresponder磷杏,還有就是 scrollview 二次封裝的 onScroll,當(dāng)然慈格,也可能有其他三方組件再次封裝遥金,但原理都是一樣的

panresponder 舉例稿械,綁定監(jiān)聽(tīng)函數(shù)會(huì)返回兩個(gè)參數(shù),具體看官網(wǎng)

onPanResponderMove: (nativeEvent, gestureState) => {}

gestureState = {
    ....
    dx - 從觸摸操作開(kāi)始時(shí)的累計(jì)橫向路程
    dy - 從觸摸操作開(kāi)始時(shí)的累計(jì)縱向路程
}

比如我們現(xiàn)在要將 dx 映射到某個(gè) Animated.value

const x = new Animated.Value(0);

onPanResponderMove: Animated.event([
        null, { dx: x }
])

嗯页眯,這就完事了厢呵, event([]) 中的 [] 會(huì)傳遞回調(diào)函數(shù)參數(shù),第一個(gè)不用忿族,直接 null ,然后映射第二個(gè)错英,就是 gestureState.dx -> x隆豹,當(dāng)然璃赡,你可以從 gestureState 找其他參數(shù)進(jìn)行映射

為加深理解,再看看 onScroll碉考, 根據(jù)介紹侯谁,其函數(shù)原型是這樣的

onScroll : (event) => {}

event = {
    nativeEvent: { 
         contentInset: { bottom, left, right, top }, 
         contentOffset: { x, y }, 
         contentSize: { height, width }, 
         layoutMeasurement: { height, width }, 
         zoomScale 
   } 
}

比如要映射 Y 軸滾動(dòng)距離

const dy = new Animated.Value(0);

onScroll={Animated.event(
   [{ nativeEvent: {
        contentOffset: { y: dy }
   }}]
)}

嗯,就是這樣热芹,參數(shù)對(duì)參數(shù)

響應(yīng)

輸入值 雖然無(wú)法自定義函數(shù)進(jìn)行計(jì)算惨撇,但好歹提供了 合成 和 插值的辦法魁衙,輸出值,就是當(dāng)前的 Animated.Value 實(shí)際值纺棺,有么有辦法監(jiān)聽(tīng)祷蝌?

RN 提供了一些方法,具體可參見(jiàn) animatedvalue米丘、animatedvaluexy糊啡;使用方法比較簡(jiǎn)單

const an = new Animated.Value(0);

an.addListener(v => {
});

// 也可直接設(shè)置值
an.setValue(2)

這種響應(yīng)是不建議直接參與動(dòng)畫(huà)的,適合作為旁觀者靜靜看著動(dòng)畫(huà)堕扶,當(dāng)達(dá)到某一個(gè)符合條件的值,觸發(fā)一些其他操作典尾。

收尾

最后再加上 layoutanimation 便是 RN動(dòng)畫(huà) 的全部?jī)?nèi)容了糊探,該 API 是做整體動(dòng)畫(huà)的科平,不是特別常用,就沒(méi)展開(kāi)髓考,需要的話可以自行查閱汞贸。

本篇文檔都是使用 RN 中文網(wǎng)的鏈接印机,該網(wǎng)站是第三方進(jìn)行翻譯的射赛,質(zhì)量不錯(cuò),但相對(duì)于 官網(wǎng) 還是有所不足楣责,比如上面說(shuō)的 animatedvalue API秆麸,由于官網(wǎng)上沒(méi)有相關(guān)入口,所以中文網(wǎng)也沒(méi)有屯烦,但在官網(wǎng)頂部的 search 中是可以搜索到的房铭,中文網(wǎng)則搜索不到 :) 英文水平跟得上缸匪,直接看官方文檔,更新會(huì)比較及時(shí)

最后凌蔬,就是 github 上的 react-native-website ,也就是官網(wǎng)的的托管庫(kù)懈词,其實(shí)也是一個(gè)不錯(cuò)的尋找文檔的地方钦睡。

最最后,這里還有一個(gè)不錯(cuò)的學(xué)習(xí)資料:https://future-challenger.gitbooks.io/react-native-animation/content/

最最最后洒琢,官方推薦的導(dǎo)航組件 react-navigation 使用一個(gè)第三方動(dòng)畫(huà)庫(kù) react-native-reanimated褐桌,該庫(kù)更為強(qiáng)大荧嵌,這里就不再介紹了,有興趣的朋友可以研究一下

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末谭网,一起剝皮案震驚了整個(gè)濱河市赃春,隨后出現(xiàn)的幾起案子织中,更是在濱河造成了極大的恐慌,老刑警劉巖层坠,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件破花,死亡現(xiàn)場(chǎng)離奇詭異采盒,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)尺栖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)延赌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人者蠕,你說(shuō)我怎么就攤上這事掐松。” “怎么了抡句?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵待榔,是天一觀的道長(zhǎng)流济。 經(jīng)常有香客問(wèn)我,道長(zhǎng)雕憔,這世上最難降的妖魔是什么橘茉? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任姨丈,我火速辦了婚禮蟋恬,結(jié)果婚禮上趁冈,老公的妹妹穿的比我還像新娘。我一直安慰自己沐绒,他們只是感情好旺坠,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布取刃。 她就那樣靜靜地躺著出刷,像睡著了一般坯辩。 火紅的嫁衣襯著肌膚如雪漆魔。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,713評(píng)論 1 312
  • 那天淌喻,我揣著相機(jī)與錄音裸删,去河邊找鬼阵赠。 笑死,一個(gè)胖子當(dāng)著我的面吹牛匕荸,可吹牛的內(nèi)容都是我干的枷邪。 我是一名探鬼主播,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼嘶卧!你這毒婦竟也來(lái)了芥吟?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤钉稍,失蹤者是張志新(化名)和其女友劉穎贡未,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體缸托,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡俐镐,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年佩抹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了取董。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡枢里,死狀恐怖栏豺,靈堂內(nèi)的尸體忽然破棺而出豆胸,到底是詐尸還是另有隱情,我是刑警寧澤灵奖,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布估盘,位于F島的核電站忿檩,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜辨图,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一故河、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧理盆,春花似錦、人聲如沸衷快。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至邓萨,卻和暖如春菊卷,著一層夾襖步出監(jiān)牢的瞬間的烁,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工铃芦, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留襟雷,地道東北人耸弄。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓计呈,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親捌显。 傳聞我的和親對(duì)象是個(gè)殘疾皇子扶歪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容