需求小能手——平滑滚动

187 阅读2分钟

前言

日常开发中我们经常遇到滚动条,当我们想要滚动条滚动到某个位置,可以直接设置元素scrollTo的值,不过该方法效果看起来很生硬,下面我们来自定义一个滚动方法,让效果看起来丝滑点。

滚动方法

我们想要滚动更加丝滑、可控,可以利用微积分的思路,把总的滚动位置分成n小份,每次只滚动一小份的距离,就能达到我们想要的效果。根据以上思路我们确定三个参数:

  • toPosition:滚动条滚动的位置。
  • duration:滚动的时间,达到我们可控的目的,该参数可以设置一个默认值。
  • callback:回调函数,滚动后完成某些时间,当然该参数肯定可选。
    function scrollTo (toPosition: number, duration: number, callback?: () => {}){}

第一步获取当前滚动条的位置,通过元素的sctollTo获取,考虑到页面嵌套情况,可以设置多种情况:

   function getStartPosition() {
  return (
    document.body.scrollTop ||
    document.documentElement.scrollTop ||
    (document.body.parentNode as HTMLElement).scrollTop
    
  )
}

我们将每一份移动的方法依旧是利用scrollTo:

   function move(position: number){
   document.body.scrollTop = position
  document.documentElement.scrollTop = position
  ;(document.body.parentNode as HTMLElement).scrollTop = position
}

下面就是重点获取位移距离,我们想要的平滑效果其实跟animation的曲线运动效果其实是一样的,所以我们可以利用animation曲线运动的函数,根据这个函数得到位移距离。下面我们就选用ease-in-out作为效果:

 //t 当前时间 b开始位移 c结束位移 d总时间
 function easeInOut(t: number, b: number, c: number, d: number){
  t /= d / 2
  if (t < 1) {
    return (c / 2) * t * t + b
  }
  t--
  return (-c / 2) * (t * (t - 2) - 1) + b
}

以上b、c、d都是固定的,简单理解就是数学中的一个函数,传入t得到距离。当前时间从0开始,设置自己想要的步长,用当前时长与总时长做对比,如果小于就将时间加上步长重复上面行为,否则执行回调函数。很明显这里我们需要用到迭代,并且为了更好的动画效果,采用requestAnimationFrame方法:

  function requestAnimFrame(function () {
  //如果requestAnimationFrame获取不到就用setTimeout
  return (
    window.requestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    (window as any).mozRequestAnimationFrame ||
    function (callback) {
      window.setTimeout(callback, 1000 / 60)
    }
  )
})()

通过以上分析,我们就能得到想要的平滑滚动函数:

  function scrollTo(
  toPosition: number,
  duration: number,
  callback?: () => {}
) {
  const start = getStartPosition()
  const change = to - start
  const step = 20
  let currentTime = 0
  duration = typeof duration === 'undefined' ? 500 : duration
  const animateScroll = function () {
    currentTime += step
    const val = easeInOutQuad(currentTime, start, change, duration)
    move(val)
    if (currentTime < duration) {
      requestAnimFrame(animateScroll)
    } else {
      if (callback && typeof callback === 'function') {
        callback()
      }
    }
  }
  animateScroll()
}

总结

以上就是平滑滚动实现的过程,重点是获取位移距离的函数,利用了animation曲线速度的函数,我们可以根据自己的需要去调用对应的函数。