vue前端框架vueuse的useScroll函數使用源碼分析

引言

頁面很多時候都含有可滾動視圖區域,可能是橫向滾動也可能是縱向滾動。

  • 有時我們需要知道當前的滾動方向,是向左還是向右,是向上還是向下;
  • 有時需要知道當前是否是正在滾動,如果滾動則顯示一個加載動畫等;
  • 有時我們還需要知道滾動條是否已經滾動到瞭上下左右的邊界。

如果我們自己來實現這一系列的邏輯判斷可能也不難,但是如何優雅地實現以便於更方便地使用呢?一起研究一下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這些值的含義。

以上就是vueuse的useScroll函數源碼分析的詳細內容,更多關於vueuse useScroll函數的資料請關註WalkonNet其它相關文章!

推薦閱讀: