react?native?reanimated實現(xiàn)動畫示例詳解
背景
在一次 App 迭代中,UI 想要給按鈕添加一個動畫效果,在對接的過程中,UI 表示直接用 .gif 就好,因為感覺開發(fā)出來的效果應該不會很好。
聽到這里,一個技術人的自尊心仿佛被踩在了地上,我當即表示想用 react-native-reanimated(下文簡稱 Reanimated) 試一試。??
動畫拆分
首先,從最外層來看,動畫有一個抖動效果:先向左,再向右??梢岳?rotate 旋轉屬性來實現(xiàn)。
其次,中間的文字部分有一個縮放動畫,可以通過 scale 實現(xiàn)。
最后,當文字最小化時,會改變內容,這個需要配合 JS 來實現(xiàn)。
實現(xiàn)抖動
首先通過 useSharedValue
定義一個共享值 rotation
,共享值和 useRef
類似,區(qū)別是共享值有一個 value
屬性而不是 current
。
我們使用共享值改變的樣式通過 useAnimatedStyle
包裝一下,再賦值給 Animated.View
,這和使用普通的 React Native 樣式有點區(qū)別:
import Animated from 'react-native-reanimated'; const rotation = useSharedValue(0); const shakeStyle = useAnimatedStyle(() => { return { transform: [ { rotateZ: `${rotation.value}deg`, }, ], }; }, []); <Animated.View style={[styles.btn, shakeStyle]}> ... </Animated.View>
定義動畫
每個動畫可以使用 withTiming
更新共享值,并設置動畫的具體參數(shù)。它會啟動基于時間的動畫曲線,如執(zhí)行時間 duration,緩動函數(shù) easing 等。
抖動的過程有三個步驟:向左旋轉,向右旋轉,保持水平。我們使用 withSequence
來編排動畫的順序。
最后,我們使用 withRepeat
讓以上三個步驟無限循環(huán)。它接受三個參數(shù):
- 第一參數(shù)是動畫函數(shù);
- 第二個參數(shù)是執(zhí)行的次數(shù),-1 表示無限次;
- 第三個參數(shù)表示動畫是否反向執(zhí)行。
注意,在恢復水平后,按鈕仍保持一段時間的靜止,我們可以用到 withDelay
來延遲執(zhí)行下一個動作。
const SCOPE = 2; useEffect(() => { const turnL = withDelay(1400, withTiming(-SCOPE, { duration: 100, easing: Easing.linear })); // 向左 const turnR = withTiming(SCOPE, { duration: 100, easing: Easing.linear }); // 向右 const holden = withTiming(0, { duration: 100, easing: Easing.linear }); // 水平 const rotateAnimations = withSequence(turnL, turnR, holden); // 編排動畫順序 rotation.value = withRepeat(rotateAnimations, -1); // 重復執(zhí)行動畫 return () => { cancelAnimation(rotation); }; }, []);
實現(xiàn)縮放動畫
實現(xiàn)縮放動畫的思路與上面基本相似。這里需要注意的是,需要根據(jù)實際需求,調整動畫之間的節(jié)奏關系。比如縮放開始,抖動開始;縮放結束,抖動也就結束。
const scaleSize = useSharedValue(0.2); const scaleStyle = useAnimatedStyle(() => { return { transform: [ { scale: scaleSize.value, }, ], }; }, []); useEffect(() => { ... const zoomOut = withDelay(1600, withTiming(0.2, { duration: 100, easing: Easing.linear })); const restoreSize = withTiming(1, { duration: 100, easing: Easing.linear }); const scaleAnimations = withSequence(restoreSize, zoomOut); scaleSize.value = withRepeat(scaleAnimations, -1); return () => { ... cancelAnimation(scaleSize); }; }, []) <Animated.View style={[styles.textWrapper, scaleStyle]}> ... <Animated.View>
改變內容
當我們依賴共享值的變化,需要進一步操作時,可以使用 useAnimatedReaction
,它第一參數(shù)中定義依賴的值,第二個參數(shù)接受第一個參數(shù)的返回值,并進行自定義的操作。
注意,共享值變化不會觸發(fā) JS 線程中的組件更新,改變文案的狀態(tài)需要用到 useState
,因為文案改變是在 JS 線程中處理的,可以通過 runOnJS
可以讓函數(shù)在 JS 線程中執(zhí)行。
import { useAnimatedReaction, runOnJS } from 'react-native-reanimation'; ... const [status, setStatus] = useState(true); const scaleSize = useSharedValue(0.2); const toggle = useCallback(() => { setStatus((s) => !s); }, []); useAnimatedReaction( () => { return scaleSize.value; }, (next) => { if (next <= 0.2) { runOnJS(toggle)(); } } ); ... <Text>{status ? '參與話題' : '賺點贊次數(shù)'}</Text>
Reanimated 原理淺析
在開發(fā)過程中,我們的動畫代碼和狀態(tài)代碼都是用 JavaScript 寫在同一個文件中的,你可能會認為你寫的動畫部分的 JavaScript 和狀態(tài)部分的 JavaScript 都是運行在同一個線程中的, 但其實并不是這樣的。
React Native 有兩個常用的線程:一個是 React Native 的 JavaScript 線程,另一個是 UI 主線程。
一方面,JavaScript 線程和 UI 主線程是異步通信的,這也意味著,如果是由 JavaScript 線程發(fā)起動畫的執(zhí)行,UI 線程并不能同步地收到該命令并且立刻執(zhí)行。
另一方面,JavaScript 線程處理的事件很多,包括所有的業(yè)務邏輯、React Diff、事件響應等,容易搶占動畫的執(zhí)行資源。
Reanimated 是如何優(yōu)化?答案就是:把動畫代碼放到 UI 主線程來執(zhí)行性能更好、不易卡頓。
它把動畫相關的 JavaScript 函數(shù)及其上下文傳給了 UI 主線程。由于 UI 主線程沒有能夠運行 JavaScript 的環(huán)境,于是 Reanimated 又創(chuàng)建了一個 JavaScript 虛擬機來運行傳過來的 JavaScript 函數(shù)。
在 JavaScript 線程中包括了三個動畫相關的函數(shù)或值, useSharedValue
、 useAnimatedStyle
和 useAnimatedGestureHandler
。
這三部分的代碼會在其底層,將相關的回調函數(shù)標記為worklet
,被標記的worklet
函數(shù)或值會被放在一個由 Reanimated 創(chuàng)建的 JavaScript 虛擬機中執(zhí)行。而這個由 Reanimated 創(chuàng)建的 JavaScript 虛擬機,會在 UI 線程中執(zhí)行傳過來的worklet
函數(shù),并且執(zhí)行的函數(shù)還可以同步地操作 UI。
Reanimated 動畫性能好的原因就在于:React Native 的 JavaScript 線程是性能瓶頸點,而在真正執(zhí)行動畫時,已經(jīng)把所有與動畫相關 JavaScript 函數(shù)都放到了 UI 線程中獨立的 JavaScript 虛擬機中了,并不會和 JavaScript 線程搶占硬件資源。
總結
Reanimated 處理動畫的方法非常巧妙,并且性能極佳,是目前 React Native 社區(qū)中主流的動畫處理方案,很多開源庫都在使用。雖然官方文檔有些缺陷,比如 API 沒有 demo 不夠直觀、目前只有英文文檔,對英文差的同學不夠友好。
未來我們將深入學習和使用 Reanimated,來提升用戶體驗,實現(xiàn)媲美原生的交互效果。
以上就是react native reanimated實現(xiàn)動畫示例詳解的詳細內容,更多關于react native reanimated 動畫的資料請關注腳本之家其它相關文章!
相關文章
react?hooks?UI與業(yè)務邏輯分離必要性技術方案
這篇文章主要為大家介紹了react?hooks?UI與業(yè)務邏輯分離必要性技術方案詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11react項目中使用react-dnd實現(xiàn)列表的拖拽排序功能
這篇文章主要介紹了react項目中使用react-dnd實現(xiàn)列表的拖拽排序,本文結合實例代碼講解react-dnd是如何實現(xiàn),代碼簡單易懂,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-02-02解析react?函數(shù)組件輸入卡頓問題?usecallback?react.memo
useMemo是一個react hook,我們可以使用它在組件中包裝函數(shù)??梢允褂盟鼇泶_保該函數(shù)中的值僅在依賴項之一發(fā)生變化時才重新計算,這篇文章主要介紹了react?函數(shù)組件輸入卡頓問題?usecallback?react.memo,需要的朋友可以參考下2022-07-07react native實現(xiàn)往服務器上傳網(wǎng)絡圖片的實例
下面小編就為大家?guī)硪黄猺eact native實現(xiàn)往服務器上傳網(wǎng)絡圖片的實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08React?中?memo?useMemo?useCallback?到底該怎么用
在React函數(shù)組件中,當組件中的props發(fā)生變化時,默認情況下整個組件都會重新渲染。換句話說,如果組件中的任何值更新,整個組件將重新渲染,包括沒有更改values/props的函數(shù)/組件。在react中,我們可以通過memo,useMemo以及useCallback來防止子組件的rerender2022-10-10