前言
動畫對於客戶端來說是非常重要的一部分,直接影響到應用的用戶體驗。前端對於動畫優化通常使用CSS3樣式來實現動畫,以利用GPU加速特性。而React-Native由於渲染模式的不同,無法使用CSS樣式的方式優化。
React-Native的實現
React-Native在動畫方面有兩個主要方式,一個是Animated,一個是LayoutAnimation。
Animated
Animated
動畫庫的原理是由JavaScript來進行動畫的計算,然后在每幀設置對應組件的style來實現動畫過程(requestAnimationFrame
)。
這個動畫庫的特點是非常靈活,因為所有的控制都是通過JavaScript實現的,動畫補幀都是在JavaScript端完成,可以實現各種復雜動畫效果,包括跟手動畫等。
但是成也JavaScript敗也JavaScript,JavaScript是單線程的,動畫的特點是要保證流暢就要保證每秒60的幀率,也就是說如果在16ms內處理不完,勢必要掉幀。而在實際業務中,播放動畫的同時一般都會執行一些任務處理邏輯。比如數據拉取、數據計算,典型情況通常會伴有Loading動畫,切換頁面過場時伴隨新頁面數據加載等。在復雜場景下,由於業務邏輯的加入,動畫處理的幀率就很難保證,用我們QA的話說就是卡的像個Web。。。
優點:
- 可實現復雜動畫
- 非常靈活
- 可以兼容Web
缺點:
- 性能問題,掉幀、卡頓
LayoutAnimation
React-Native自帶還有一種動畫的實現方式,就是LayoutAnimation
。這種方案是直接在Native實現一些動畫效果,然后由JavaScript進行設置調用,由於整個動畫過程交由Native處理,使得性能得以保證。不過LayoutAnimation
的實現思路有點問題,他只是預置了幾個動畫效果,並且只能配置在create/update/delete的時候,需要自定義的動畫、跟手動畫等都沒法實現。
優點:
- 性能良好
缺點:
- 動畫效果簡單,可適用范圍較窄
嘗試解決
給他特權
針對Animated
的優化思路就是解決動畫播放時JavaScript任務量的問題,保證動畫循環達到60幀。一個典型的解決方案就是暫時停止所有同步任務的處理,等到動畫執行完成之后再同步或異步的執行任務處理。比如在切頁轉場前暫停耗時操作(延遲Redux的dispatch過程等),轉場動畫結束后再進行同步或異步處理,把大的同步任務用setTimeout
等方式拆成多個異步過程處理。
這種方案可以一定程度上解決動畫卡頓的問題,但是有幾個缺點:
- 任務總是要執行的,延遲處理會導致整個處理過程變長,響應用戶操作速度變慢,影響用戶體驗。
- 治標不治本,復雜動畫該卡還得卡。。。
優點:
- 改動較少,成本低,萬一以后facebook改進了呢?比如在獨立線程進行動畫渲染計算?(需要解決數據同步問題,WebWorker也是個坑)
- 可以實現跟手動畫,控制好JavaScript的任務量,效果還可以
所以此方案適用 動畫相對簡單、后續任務不復雜 的場景
原生優化
要從根本上解決問題,就需要讓動畫過程脫離JavaScript。所以就要開發原生組件來實現動畫過程,這樣可以充分利用機器性能,可以使用GPU進行動畫渲染。還需要考慮靈活性,如果只能實現固定的效果那么使用場景就大打折扣。
然而,我們並不需要從頭設計,OC、Android、WPF,都有優秀的動畫API,我們還等什么?抄吧。
(So,歡迎使用 react-native-animation 支持iOS、Android,目前驗證可行、能用、不完善、待重構,歡迎PR~)
舉個小栗子:
<Animation.AnimationView
data={[{
type: 'Alpha',
from: 0,
to: 1,
duration: 500,
}]} autoplay={true}>
other views...
</Animation.AnimationView>
*Now support: 'Translate' | 'Rotate' | 'Scale' | 'Alpha'
Props:
export interface PropsDefine {
data: AnimationModel[]
style?: React.ViewStyle
autoplay?: boolean
autoclear?: boolean
onStart?: (view: AnimationView) => void
onEnd?: (view: AnimationView) => void
}
數據模型:
export interface AnimationModel {
name?: string
type: 'Translate' | 'Rotate' | 'Scale' | 'Alpha'
from?: number
to?: number
from2?: number
to2?: number
duration: number
startOffset?: number
interpolator?: 'Linear'
interpolatorData?: number
repeat?: number
}
效果演示:
iOS微粉App(android版兼容開發中),業務邏輯全React-Native實現。贊踩動畫、彈幕動畫、切頁動畫、回復/評論彈層動畫已應用優化。
雖然這種方案可以解決可預測的動畫得卡頓問題,缺點也很明顯:
- 動畫的過程是預設好的,所以無法實現跟手動畫
優點:
- 動畫過程交由Native處理,性能Max,JavaScript任務處理不影響動畫效果,可並行
所以此方案適用 可預測的動畫 的場景
結語
雖然React-Native標榜的就是性能更好,但也只是跟Web對比而言,跟Native比還是有不少差距的,跟Xamarin等跨平台方案對比性能也差距不小,所以在開發時還是要關注下性能問題,性能調優還是要花不少精力去解決的。
微粉iOS: