前言
本文旨在介紹React Native ScrollView官方文檔中未提及的滾動事件API,著重介紹如何從物理學(xué)角度理解onMomentumScrollBegin 和onMomentumScrollEnd函數(shù)。
至于官方文檔中為何不提及這些API,個人理解是目前文檔也在不斷更新,文檔還未完善。亦或是我打開的姿勢不對?希望知道的朋友能科普下,解鎖新的姿勢。
ScrollView簡介
一個包裝了平臺的ScrollView(滾動視圖)的組件,同時還集成了觸摸鎖定的“響應(yīng)者”系統(tǒng)。
記住ScrollView必須有一個確定的高度才能正常工作,因?yàn)樗鼘?shí)際上所做的就是將一系列不確定高度的子組件裝進(jìn)一個確定高度的容器(通過滾動操作)。要給一個ScrollView確定一個高度的話,要么直接給它設(shè)置高度(不建議),要么確定所有的父容器都已經(jīng)綁定了高度。在視圖棧的任意一個位置忘記使用{flex:1}都會導(dǎo)致錯誤,你可以使用元素查看器來查找問題的原因。
ScrollView內(nèi)部的其他響應(yīng)者尚無法阻止ScrollView本身成為響應(yīng)者。
官方文檔介紹的API
官方文檔中提及了三個與事件回調(diào)相關(guān)的API,如下所示:

官方文檔未介紹的API
此處只列出ScrollView特有的事件回調(diào)API,關(guān)于“響應(yīng)者系統(tǒng)”的回調(diào)API一下就不列出了。
| API | Description |
|---|---|
| onTouchStart | 按下屏幕時觸發(fā) |
| onTouchMove | 移動時觸發(fā),實(shí)際上按住不動也會觸發(fā) |
| onTouchEnd | 手指離開屏幕時觸發(fā),Android測試當(dāng)有拖曳行為時會調(diào)用onScrollEndDrag來代替之;當(dāng)無拖曳行為時,手指離開就會觸發(fā) |
| onScrollBeginDrag | 開始拖動時觸發(fā),滾動開始的標(biāo)志 |
| onScroll | 滾動過程中調(diào)用,一幀最多調(diào)用一次 |
| onScrollEndDrag | 拖曳結(jié)束時調(diào)用,頂替onTouchEnd |
| onMomentumScrollBegin | 手指離開屏幕時調(diào)用(瞬時沖量滑動的開始) |
| onMomentumScrollEnd | 滾動結(jié)束時調(diào)用(瞬時沖量滑動的結(jié)束) |
起初很不理解最后倆個方法的描述,網(wǎng)上一查資料,解釋為“一幀滾動的開始結(jié)束”,“幀滾動”是什么鬼?一臉懵逼,我拖曳的時候畫面不是一直在滾動嗎?意思會調(diào)用好幾次?百度、google無果,只能一臉懵逼的自己寫Demo驗(yàn)證了。
Momentum一詞指物理學(xué)中動量,再解釋之前,我們先回顧下高中所學(xué)的物理知識。
動量定理:

瞬時沖量:極短時間內(nèi)力F產(chǎn)生的沖量,簡單點(diǎn)就理解為物體的速度會突變,舉個例子就是你給一個靜止的物體,施加一個瞬時沖量,那瞬間,物體的速度會從0突變到另外一個值,而位移未曾改變。
結(jié)論:這組函數(shù)是用于響應(yīng)你手指在離開屏幕那一剎那(極短時間),對物體在滑動反方向上所施加力的作用效果,這時會產(chǎn)生一個瞬時沖量,至使物體速度突變,之后的滑動過程就是一個減速運(yùn)動至速度為零(ScrollView內(nèi)部會模擬產(chǎn)生一個摩擦力)。
這就是為什么在ScrollView的實(shí)際操作當(dāng)中,當(dāng)我們用力滑動一小截,ScrollView就會連翻好幾頁的原因。
簡而言之:
onMomentumScrollBegin會在你手指離開屏幕時調(diào)用,不過在此之前會先調(diào)用onScrollDragEnd,onMomentumScrollEnd會在滑動結(jié)束時調(diào)用。
Demo測試驗(yàn)證
測試情景一:
手指按住,不移動(持續(xù)1-2s),然后離開屏幕,測試結(jié)果如下如所示:

可見onTouchMove在測試期間共調(diào)用了35次,并不是如網(wǎng)上所言在移動時候調(diào)用,而是在onTouchStart后就會調(diào)用,手指離開屏幕就會調(diào)用onTouchEnd結(jié)束事件響應(yīng)。
注意,onTouchMove傳進(jìn)來的nativeEvent并沒有contentOffset屬性(圖片相對ScrollView容器的滑動偏移量)如圖-4所示,這也從另外一個方面說明它不是在移動的時候調(diào)用。

測試情景二:
手指按住屏幕,慢慢的拖動一段距離,再最后要離開屏幕時,手指往滑動反方向用力,測試結(jié)果如下所示,圖中數(shù)字表示滑動方向的偏移量:

上面說到onTouchMove并不是在滑動時調(diào)用,那么滑動開始回調(diào)API是誰呢?聰明的你肯定想到了,沒錯就是onScrollBeginDrag,代表滑動的開始。該函數(shù)接收的nativeEvent才包含了contentOffset屬性,如圖-5所示。

onScroll就是在滑動的過程中會調(diào)用。
重點(diǎn)來了,
在你手指離開屏幕時,會先調(diào)用onScrollEndDrag,表示拖動結(jié)束,接下來,就如上面我們分析的一樣,根據(jù)你手指離開屏幕時所施加的力,計算這個瞬時沖量該讓圖片滑動多少距離。依次調(diào)用onMomentumScrollBegin,onScroll,onMomentumScrollEnd。
測試代碼就不上了,太長了,寫起來也很簡單。
最后,由于本人水平有限,第一次寫文章,文中難免有不妥之處,還請多多體諒。