banner
 Sayyiku

Sayyiku

Chaos is a ladder
telegram
twitter

優雅實現 BackTop

BackTop 即滾動到頁面頂部,是很多網站都會用到的基礎功能,實現方法很多,Github 上也有許多優秀的三方庫,如 smooth-scroll,但如何優雅實現也是一門學問。

事件綁定和解绑#

滾動到頁面頂部的按鈕一般位於頁面角落,並且只有在需要的時候才顯示出來。所以首先需要監聽頁面滾動事件,直到滾動到一定距離後顯示 BackTop 按鈕。

監聽頁面滾動最簡單的實現方式是使用 addEventListener 監聽 scroll 事件,並在頁面卸載時解除監聽,代碼如下:

window.addEventListener('scroll', handleScroll, false)
window.removeEventListener('scroll', handleScroll, false)

但既然稱為最優雅的實現方式,為了兼任各種瀏覽器,可以將綁定和解绑事件提取出公共方法,並作兼容優化,代碼如下:

/**
 * @description 綁定事件 on(element, event, handler)
 */
export const on = (function() {
  if (document.addEventListener) {
    return function(element, event, handler) {
      if (element && event && handler) {
        element.addEventListener(event, handler, false)
      }
    }
  } else {
    return function(element, event, handler) {
      if (element && event && handler) {
        element.attachEvent('on' + event, handler)
      }
    }
  }
})()

/**
 * @description 解绑事件 off(element, event, handler)
 */
export const off = (function() {
  if (document.removeEventListener) {
    return function(element, event, handler) {
      if (element && event) {
        element.removeEventListener(event, handler, false)
      }
    }
  } else {
    return function(element, event, handler) {
      if (element && event) {
        element.detachEvent('on' + event, handler)
      }
    }
  }
})()

調用方式:

on(window, 'scroll', handleScroll)
off(window, 'scroll', handleScroll)

function handleScroll() {
  console.log(window.pageYOffset)
}

回到頂部動畫#

window.requestAnimationFrame() 方法請求瀏覽器在下一次重繪之前調用指定的函數來更新動畫。該方法使用一個回調函數作為參數,這個回調函數會在瀏覽器重繪之前調用。回調的次數通常是每秒 60 次。由於兼容問題,在不同瀏覽器需要帶上前綴,並且在瀏覽器不支持時使用 setTimeout 模擬。

requestAnimationFrame 目的是為了讓各種網頁動畫效果(DOM 動畫、Canvas 動畫、SVG 動畫、WebGL 動畫)能夠有一個統一的刷新機制,從而節省系統資源,提高系統性能,改善視覺效果。

使用 requestAnimationFrame 來實現滾動到頁面頂部的動畫,核心是按幀來滾動小段距離來實現平滑滾動的效果,代碼如下:

// scrollTop animation
export function scrollTop(el, from = 0, to, duration = 500, endCallback) {
  if (!window.requestAnimationFrame) {
    window.requestAnimationFrame =
      window.webkitRequestAnimationFrame ||
      window.mozRequestAnimationFrame ||
      window.msRequestAnimationFrame ||
      function(callback) {
        return window.setTimeout(callback, 1000 / 60)
      }
  }
  const difference = Math.abs(from - to)
  const step = Math.ceil((difference / duration) * 50)

  function scroll(start, end, step) {
    if (start === end) {
      endCallback && endCallback()
      return
    }

    let d = start + step > end ? end : start + step
    if (start > end) {
      d = start - step < end ? end : start - step
    }

    if (el === window) {
      window.scrollTo(d, d)
    } else {
      el.scrollTop = d
    }
    window.requestAnimationFrame(() => scroll(d, end, step))
  }
  scroll(from, to, step)
}

調用方式:

function backTop() {
  const sTop = document.documentElement.scrollTop || document.body.scrollTop
  scrollTop(window, sTop, 0, 2000)
}

擴展:該 API 還提供 cancelAnimationFrame 方法用來取消重繪,參數是 requestAnimationFrame 返回的一個代表任務 ID 的整數值,使用如下:

const requestID = window.requestAnimationFrame(() => scroll(d, end, step))
window.cancelAnimationFrame(requestID)

如果不需要考慮瀏覽器兼容性,在 Chrome、Firefox 瀏覽器上,window.scrollTo 還支持第二種參數形式,傳入參數 options 是一個包含三個屬性的對象:

  • top 等同於 y-coord,代表縱軸坐標
  • left 等同於 x-coord,代表橫軸坐標
  • behavior 類型 String,表示滾動行為,支持參數 smooth(平滑滾動),instant(瞬間滾動),默認值 auto,效果等同於 instant
window.scrollTo({
  top: 0,
  behavior: 'smooth'
})

此方法簡單高效,可惜 Edge、IE、Safari 皆不支持。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。