better-scroll-blog icon indicating copy to clipboard operation
better-scroll-blog copied to clipboard

momentum(惯性滚动实现原理)

Open proc07 opened this issue 6 years ago • 0 comments

何时进行惯性滚动?

作用:当快速在屏幕中滑动一段距离后(手指离开),根据这段滑动的时间和距离代入到公式,求出最后滚动到的位置,并进行滚动动画。

_end 方法中调用了 momentum 来计算最后的位置。

// start momentum animation if needed
/* 必须开启 momentum 配置,滑动的时间小于 momentumLimitTime,且滑动的距离要超过  momentumLimitDistance (px)位置 */
if (this.options.momentum && duration < this.options.momentumLimitTime && (absDistY > this.options.momentumLimitDistance || absDistX > this.options.momentumLimitDistance)) {
  let momentumX = this.hasHorizontalScroll ? momentum(this.x, this.startX, duration, this.maxScrollX, this.options.bounce ? this.wrapperWidth : 0, this.options)
        : {destination: newX, duration: 0}
  let momentumY = this.hasVerticalScroll ? momentum(this.y, this.startY, duration, this.maxScrollY, this.options.bounce ? this.wrapperHeight : 0, this.options)
        : {destination: newY, duration: 0}
  newX = momentumX.destination
  newY = momentumY.destination
  time = Math.max(momentumX.duration, momentumY.duration)
  this.isInTransition = true
 } else {
   // code...
 }
  • momentum 方法中传入了一大推参数,下面看如何使用!

momentum.js

/**
 *  current  结束位置
 *  start    开始位置
 *  time     时长
 *  lowerMargin 滚动区域 maxScroll
 *  wrapperSize 当滚动超过边缘的时候会有一小段回弹动画 (wrapperSize = bounce)
 *
 *  distance 距离
 *  speed    速度
 *  deceleration 减速度(系数) 0.001
 *  destination  目的地
 */
export function momentum(current, start, time, lowerMargin, wrapperSize, options) {
  let distance = current - start
  let speed = Math.abs(distance) / time

  let {deceleration, itemHeight, swipeBounceTime, wheel, swipeTime} = options
  let duration = swipeTime
  //  wheel: 为 picker 组件时,加大回弹系数 
  let rate = wheel ? 4 : 15
  //  公式:惯性拖拽 = 最后的位置 + 速度 / 摩擦系数 * 方向
  let destination = current + speed / deceleration * (distance < 0 ? -1 : 1)
  //  picker 组件时,将计算的位置取整
  if (wheel && itemHeight) {
    destination = Math.round(destination / itemHeight) * itemHeight
  }
  // 目的地(超过)最大的滚动范围 maxScroll
  if (destination < lowerMargin) {
    // 是否开启回弹
    destination = wrapperSize ? lowerMargin - (wrapperSize / rate * speed) : lowerMargin
    duration = swipeBounceTime
  } else if (destination > 0) {
    destination = wrapperSize ? wrapperSize / rate * speed : 0
    duration = swipeBounceTime
  }
  // 如果未触发以上两种条件(未到达边界),则使用最初计算出来的位置

  return {
    destination: Math.round(destination),
    duration
  }
}
  • 得出值后,在使用 scrollTo 进行滚动。

小结

现在,我们已经知道了这个 momentum 函数的实现,公式:惯性拖拽 = 最后的位置 + 速度 / 摩擦系数 * 方向。

proc07 avatar Mar 10 '18 03:03 proc07