前言
本文旨在介绍React Native ScrollView官方文档中未提及的滚动事件API,着重介绍如何从物理学角度理解onMomentumScrollBegin 和onMomentumScrollEnd函数。
至于官方文档中为何不提及这些API,个人理解是目前文档也在不断更新,文档还未完善。亦或是我打开的姿势不对?希望知道的朋友能科普下,解锁新的姿势。
ScrollView简介
一个包装了平台的ScrollView(滚动视图)的组件,同时还集成了触摸锁定的“响应者”系统。
记住ScrollView必须有一个确定的高度才能正常工作,因为它实际上所做的就是将一系列不确定高度的子组件装进一个确定高度的容器(通过滚动操作)。要给一个ScrollView确定一个高度的话,要么直接给它设置高度(不建议),要么确定所有的父容器都已经绑定了高度。在视图栈的任意一个位置忘记使用{flex:1}都会导致错误,你可以使用元素查看器来查找问题的原因。
ScrollView内部的其他响应者尚无法阻止ScrollView本身成为响应者。
官方文档介绍的API
官方文档中提及了三个与事件回调相关的API,如下所示:

官方文档未介绍的API
此处只列出ScrollView特有的事件回调API,关于“响应者系统”的回调API一下就不列出了。
API | Description |
---|---|
onTouchStart | 按下屏幕时触发 |
onTouchMove | 移动时触发,实际上按住不动也会触发 |
onTouchEnd | 手指离开屏幕时触发,Android测试当有拖曳行为时会调用onScrollEndDrag来代替之;当无拖曳行为时,手指离开就会触发 |
onScrollBeginDrag | 开始拖动时触发,滚动开始的标志 |
onScroll | 滚动过程中调用,一帧最多调用一次 |
onScrollEndDrag | 拖曳结束时调用,顶替onTouchEnd |
onMomentumScrollBegin | 手指离开屏幕时调用(瞬时冲量滑动的开始) |
onMomentumScrollEnd | 滚动结束时调用(瞬时冲量滑动的结束) |
起初很不理解最后俩个方法的描述,网上一查资料,解释为“一帧滚动的开始结束”,“帧滚动”是什么鬼?一脸懵逼,我拖曳的时候画面不是一直在滚动吗?意思会调用好几次?百度、google无果,只能一脸懵逼的自己写Demo验证了。
Momentum一词指物理学中动量,再解释之前,我们先回顾下高中所学的物理知识。
动量定理:

瞬时冲量:极短时间内力F产生的冲量,简单点就理解为物体的速度会突变,举个例子就是你给一个静止的物体,施加一个瞬时冲量,那瞬间,物体的速度会从0突变到另外一个值,而位移未曾改变。
结论:这组函数是用于响应你手指在离开屏幕那一刹那(极短时间),对物体在滑动反方向上所施加力的作用效果,这时会产生一个瞬时冲量,至使物体速度突变,之后的滑动过程就是一个减速运动至速度为零(ScrollView内部会模拟产生一个摩擦力)。
这就是为什么在ScrollView的实际操作当中,当我们用力滑动一小截,ScrollView就会连翻好几页的原因。
简而言之:
onMomentumScrollBegin会在你手指离开屏幕时调用,不过在此之前会先调用onScrollDragEnd,onMomentumScrollEnd会在滑动结束时调用。
Demo测试验证
测试情景一:
手指按住,不移动(持续1-2s),然后离开屏幕,测试结果如下如所示:

可见onTouchMove在测试期间共调用了35次,并不是如网上所言在移动时候调用,而是在onTouchStart后就会调用,手指离开屏幕就会调用onTouchEnd结束事件响应。
注意,onTouchMove传进来的nativeEvent并没有contentOffset属性(图片相对ScrollView容器的滑动偏移量)如图-4所示,这也从另外一个方面说明它不是在移动的时候调用。

测试情景二:
手指按住屏幕,慢慢的拖动一段距离,再最后要离开屏幕时,手指往滑动反方向用力,测试结果如下所示,图中数字表示滑动方向的偏移量:

上面说到onTouchMove并不是在滑动时调用,那么滑动开始回调API是谁呢?聪明的你肯定想到了,没错就是onScrollBeginDrag,代表滑动的开始。该函数接收的nativeEvent才包含了contentOffset属性,如图-5所示。

onScroll就是在滑动的过程中会调用。
重点来了,
在你手指离开屏幕时,会先调用onScrollEndDrag,表示拖动结束,接下来,就如上面我们分析的一样,根据你手指离开屏幕时所施加的力,计算这个瞬时冲量该让图片滑动多少距离。依次调用onMomentumScrollBegin,onScroll,onMomentumScrollEnd。
测试代码就不上了,太长了,写起来也很简单。
最后,由于本人水平有限,第一次写文章,文中难免有不妥之处,还请多多体谅。
网友评论