vueuse useScroll函数
NewName 人气:5引言
页面很多时候都含有可滚动视图区域,可能是横向滚动也可能是纵向滚动。
- 有时我们需要知道当前的滚动方向,是向左还是向右,是向上还是向下;
- 有时需要知道当前是否是正在滚动,如果滚动则显示一个加载动画等;
- 有时我们还需要知道滚动条是否已经滚动到了上下左右的边界。
如果我们自己来实现这一系列的逻辑判断可能也不难,但是如何优雅地实现以便于更方便地使用呢?一起研究一下vueuse的useScroll函数吧~
1.示例
vueuse官方文档给出了useScroll函数的demo, 我们可以在线操作看一下效果:
如上图所示,当向下滑动或者拖拽竖直方向的滚动条时则isScrolling为true表示正在向下滚动,同时useScroll能够识别出滚动方向为向下。
如上图所示,当滚动条触底的时候,useScroll能够识别出已经到达了底部。
useScroll为何如此好用,是如何实现的呢?我们一同学习其源码。
2.源码解析
先看折叠后的代码整体了解一下:
我们发现整个代码流程包括了参数的解析,状态的定义,滚动结束回调函数,滚动监听处理函数,最后是返回值,我们依次来看一下。
2.1 参数解析
const { throttle = 0, idle = 200, onStop = noop, onScroll = noop, offset = { left: 0, right: 0, top: 0, bottom: 0, }, eventListenerOptions = { capture: false, passive: true, }, } = options
useScroll接受两个参数,第一参数为目标元素,也就是监听哪一个元素的滚动事件;第二个参数为options,涵盖其他的选项。我们来详细地看一下这些选项的含义:
- throttle 滚动事件的节流事件,默认不对滚动事件节流,所以throttle的默认值为0。
- idle 滚动结束时的检查事件,这个值会和throttle 加在一起对滚动结束事件进行防抖,分析后面的代码时会看到
- onStop 滚动结束时触发的回调函数
- onScroll 滚动时触发的回调函数
- offset 定义滚动条到达上下左右边界的一个偏移值,单位为像素。例如left设置为30, 则水平滚动条距离左边界30px时则认为到达了左边界。
- eventListenerOptions 滚动事件监听器的选项
2.2 响应式状态定义
const x = ref(0) const y = ref(0) const isScrolling = ref(false) const arrivedState = reactive({ left: true, right: false, top: true, bottom: false, }) const directions = reactive({ left: false, right: false, top: false, bottom: false, })
定义响应式变量x用于记录上次滚动的scrollLeft的值;
y记录上次滚动的scrollTop的值;
isScrolling表示是否正在滚动。
arrivedState提供了水平方向滚动条距离左边和右边的距离以及垂直方向滚动条距离上边和下边的距离。
directions用于描述当前滚动的方向。
2.3 onScrollEnd滚动结束回调
const onScrollEnd = useDebounceFn((e: Event) => { isScrolling.value = false directions.left = false directions.right = false directions.top = false directions.bottom = false onStop(e) }, throttle + idle)
滚动结束回调函数使用了useDebounceFn进行防抖。当滚动结束后,isScrolling赋值为false, 滚动方向全部赋值为false, 调用onStop回调。
2.4 onScrollHandler滚动处理
const onScrollHandler = (e: Event) => { const eventTarget = ( e.target === document ? (e.target as Document).documentElement : e.target ) as HTMLElement const scrollLeft = eventTarget.scrollLeft directions.left = scrollLeft < x.value directions.right = scrollLeft > x.value arrivedState.left = scrollLeft <= 0 + (offset.left || 0) arrivedState.right = scrollLeft + eventTarget.clientWidth >= eventTarget.scrollWidth - (offset.right || 0) x.value = scrollLeft let scrollTop = eventTarget.scrollTop // patch for mobile compatible if (e.target === document && !scrollTop) scrollTop = document.body.scrollTop directions.top = scrollTop < y.value directions.bottom = scrollTop > y.value arrivedState.top = scrollTop <= 0 + (offset.top || 0) arrivedState.bottom = scrollTop + eventTarget.clientHeight >= eventTarget.scrollHeight - (offset.bottom || 0) y.value = scrollTop isScrolling.value = true onScrollEnd(e) onScroll(e) }
onScrollHandler用于处理滚动。首先是水平方向滚动方向与是否到达左右边界的判断。说明如下:
(1)获取当前的scrollLeft和上一次的scrollLeft也就是x.value进行比较,如果scrollLeft < x.value说明向左滚动,否则向右滚动。
(2)如何判断是否到达左边界?这里考虑到了偏移量offset.left。如果当前的scrollLeft <= offset.left就认为到达了左边界。
(3)是否到达右边界要看当前滚动的距离+元素视口的宽度(clientWidth)是否大于整个内容的宽度(scrollWidth)减去偏移量的值(offset.right)
竖直方向的判断同理,不再赘述,如果您不熟悉clientWidth、scrollWidth的含义,您可以阅读笔者之前的文章:scrollTop、clientHeight、 scrollHeight...学完真的理解了。
滚动的时候还需要调用onScrollEnd触发滚动结束事件,调用onScroll回调函数,将isScrolling设置为true。
2.5 使用 useEventListener监听滚动事件
useEventListener( element, 'scroll', throttle ? useThrottleFn(onScrollHandler, throttle) : onScrollHandler, eventListenerOptions, )
使用useEventListener监听scroll事件,如果需要节流则回调函数为useThrottleFn包装过的onScrollHandler,否则直接使用onScrollHandler。
2.6 返回值
return { x, y, isScrolling, arrivedState, directions, }
useScroll最后返回响应式状态。我们使用一张图总结一下这些状态:
3.总结
useScroll提供了响应式的滚动位置和状态。滚动位置包括水平方向和垂直方向的滚动位置;滚动状态包括是否在滚动,是否到达了上下左右的边界,当前滚动的方向。useScroll使用useEventListener来监听滚动事件,在滚动事件的监听回调函数中修改状态和位置。在其计算位置的源码部分我们需要了解clientWidth、scrollWidth、clientHeight、scrollHeight这些值的含义。
加载全部内容