因为移动端原生滚动的局限性以及兼容性,部分特定场景的需求没法知足。例如,笔者最近就接了一个需求:整个页面分为三块,每块内容的高度不等(但都超过一屏),要求滚动到内容的临界点有一个停顿的效果,下拉能够看到下一块的部份内容,知足条件则滑到下一块内容。这种场景下,原生的滚动根本没法支持。所以,本文的主角就亮相了:模拟滚动,即尽量的模拟原生滚动,可是又提供了一些扩展,知足复杂场景的需求。html
本文将从模拟滚动须要实现的功能、技术分析和方案来进行阐述,通读本文,读者将对模拟滚动的常见功能和技术要点有必定了解。git
示例:模拟滚动github
经过移动端的touch
系列事件触发模拟滚动,获取手指滑动的偏移量,进而改变translateY
来进行位置偏移。浏览器
滚动容器拥有高度,滚动区域的高度大于滚动容器,在滚动时,咱们对滚动区域进行偏移,以达到滚动的视觉效果。动画
经过滚动区域的高度能够经过offsetHeight
获取,可是在如下状况下会远远小于实际高度。code
咱们知道,为了让滚动更加流畅,原生的滚动会有一个惯性滚动的效果,即手指快速滑动松开后,滚动区域会继续滚动一段距离后中止。htm
为了实现这个功能,咱们需求知道手指滑动的速度,根据比率计算目标滚动位置,而后驱动滚动,让其到达目标位置。事件
这里笔者尝试了两种方案:图片
requestAnimationFrame
不断进行偏移,直到到达目标位置transition
进行过渡,设置动画曲线让其到达目标位置笔者对比了两种方案,最终选择了方案2,缘由是transition过渡会更加的流畅,而requestAnimationFrame会有略微的卡顿,可是transition过渡,咱们实时触发滚动事件时,很差拿到其当前的位置,查阅了一些资料,笔者最终找到了解决方法,即getComputedStyle
,这个API能够拿到当前页面渲染的实时样式,也就是说,哪怕它处于过渡动画中,咱们能够实时拿到它的真实位置。get
当滚动超出边界时,一般咱们还可让其继续滚动,可是这时候会设置阻碍,即滚动速度慢下来,当滚动中止时,咱们再将其拽回到边界线。咱们能够经过监听transitionend
事件来判断惯性滚动中止,这里的技术点不作过多分析,感兴趣能够在文末中的源码找答案。
一般状况下,咱们须要阻止浏览器的默认行为(如滚动),可是这样也会误杀一些咱们须要的默认行为(如超连接跳转、输入框聚焦)。
解决方法很简单,在touchstart
触发时,咱们判断一下目标节点是否须要阻止默认行为,好比说tagName=INPUT
,咱们不阻止默认行为。
默认行为被阻止,绑定在子节点上的点击事件就没法触发了,所以这里咱们须要判断一下是否须要触发点击事件。能够经过touch
系列事件模拟点击行为,而后经过document.createEvent('Event')
来主动触发click事件。
在滚动区域中,一般在右侧会有一个指示器,用于查看当前在整个内容区块的大概位置。
为了方便使用,笔者注册了一系列的钩子,方便使用者调用,scroll
钩子就是其中之一,在滚动的时候它会实时触发,在这里就派上用场了。咱们经过scroll
钩子改变指示器的位置,惟独要注意的是滚动超出边界时,指示器会变短而后恢复。
基于以上知识点和技术分析,笔者写了一个模拟滚动js库(无任何依赖):https://github.com/ansenhuang/axe/blob/master/packages/scroller/README.md