>  기사  >  웹 프론트엔드  >  피커 플러그인 구현 방법 소개(코드)

피커 플러그인 구현 방법 소개(코드)

不言
不言앞으로
2019-03-30 11:32:183177검색

이 글은 피커 플러그인의 구현 방법(코드)을 소개합니다. 참고할 만한 가치가 있으니 도움이 필요한 분들에게 도움이 되길 바랍니다.

일반 선택기 플러그인은 매우 상세하며 단계별로 설명할 수 있습니다. 손가락이 슬라이드되면 콘텐츠가 손가락과 함께 스크롤됩니다. 콘텐츠가 아래쪽에 도달하거나 위쪽에 도달하면 스크롤할 수 없으며 콘텐츠는 항상 올바른 위치에 유지되어야 합니다.

첫 번째 단계는 플러그인 구조를 분석하는 것입니다

우선 플러그인 컨테이너가 있어야 합니다. 전체 플러그인 컨테이너에는 그라데이션 배경이 포함되어 있습니다. 효과는 다음과 유사합니다.

피커 플러그인 구현 방법 소개(코드)

그래서 해당 코드는 다음과 같습니다.

<div>
    <div></div>
    <div></div>
    <div>
        <div>1</div>
        <div>2</div>
        <div>3</div>
        <div>4</div>
        <div>5</div>
        <div>6</div>
        <div>7</div>
        <div>8</div>
        <div>9</div>
        <div>10</div>
        <div>11</div>
        <div>12</div>
        <div>13</div>
        <div>14</div>
        <div>15</div>
        <div>16</div>
        <div>17</div>
        <div>18</div>
        <div>19</div>
        <div>20</div>
    </div>
</div>
* {
    margin: 0;
    padding: 0;
}
.scroller-component {
    display: block;
    position: relative;
    height: 238px;
    overflow: hidden;
    width: 100%;
}

.scroller-content {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    z-index: 1;
}

.scroller-mask {
    position: absolute;
    left: 0;
    top: 0;
    height: 100%;
    margin: 0 auto;
    width: 100%;
    z-index: 3;
    transform: translateZ(0px);
    background-image:
        linear-gradient(to bottom, rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0.6)),
        linear-gradient(to top, rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0.6));
    background-position: top, bottom;
    background-size: 100% 102px;
    background-repeat: no-repeat;
}

.scroller-item {
    text-align: center;
    font-size: 16px;
    height: 34px;
    line-height: 34px;
    color: #000;
}

.scroller-indicator {
    width: 100%;
    height: 34px;
    position: absolute;
    left: 0;
    top: 102px;
    z-index: 3;
    background-image:
        linear-gradient(to bottom, #d0d0d0, #d0d0d0, transparent, transparent),
        linear-gradient(to top, #d0d0d0, #d0d0d0, transparent, transparent);
    background-position: top, bottom;
    background-size: 100% 1px;
    background-repeat: no-repeat;
}

.scroller-item {
    line-clamp: 1;
    -webkit-line-clamp: 1;
    overflow: hidden;
    text-overflow: ellipsis;
}

css 코드는 주로 스타일 표시로 사용되며 외부 링크를 통해 소개됩니다. 여기서는 너무 많은 설명을 하지 않겠습니다.

두 번째 단계는 손가락 스크롤 컨테이너를 구현하는 것입니다

1. 손가락 터치 이벤트를 추가합니다

let component = document.querySelector('[data-role=component]')

let touchStartHandler = (e) => { }
let touchMoveHandler = (e) => { }
let touchEndHandler = (e) => { }

component.addEventListener('touchstart', touchStartHandler)

component.addEventListener('touchmove', touchMoveHandler)

component.addEventListener('touchend', touchEndHandler)

손가락이 컴포넌트 플러그인 컨테이너에 닿으면 시작, 이동 및 종료 이벤트가 트리거됩니다.

2. 손가락이 슬라이드할 때 컨테이너의 이동 효과를 분석합니다.

손가락이 위로 슬라이드되면 콘텐츠가 위로 슬라이드되고, 손가락이 아래로 슬라이드되면 콘텐츠가 아래로 슬라이드됩니다. 손가락이 미끄러지는 거리와 일치하도록 콘텐츠의 위치가 변경되는 거리를 제어하기만 하면 됩니다. 변환 스타일의 move3d(x, y, z) 속성이 여기에서 사용됩니다. 그 중 x와 z는 변하지 않고 그대로 유지되며, y값은 손가락 움직임의 값이다.

계속해서 분해해 보겠습니다. 손가락을 아래로 당기면 동작에 맞춰 콘텐츠 위치가 아래로 이동합니다. 즉, y 값이 커집니다(y축의 양의 방향이 아래쪽을 향함). 손가락은 위로 미끄러지는 시간에 맞춰 위로 당겨집니다. 풀다운하거나 다시 풀업할 때 내용은 원래대로 유지되어야 합니다. 따라서 이 값을 저장하려면 전역 변수 __scrollTop이 필요합니다. 이 값은 매번 사용자의 풀업과 풀다운 값을 합한 것과 같기 때문에 매번 사용자의 풀업과 풀다운 값을 알아내야 합니다.

사용자가 가져온 값을 분해합니다. 사용자가 화면을 터치하면 touchstart 이벤트가 트리거되고, 사용자가 움직일 때 touchmove 이벤트가 트리거됩니다. 떠날 때 touchend 이벤트가 트리거됩니다. 사용자 풀업의 초기값은 터치스타트가 실행될 때 손가락의 위치여야 합니다. 최종 값은 터치엔드에서의 손가락 위치입니다. 하지만 이런 방식으로는 콘텐츠가 손가락의 실시간 움직임을 따라갈 수 없습니다. 따라서 touchmove 이벤트를 분해해야 합니다. touchmove 이벤트는 사용자의 손가락이 움직일 때 지속적으로 트리거됩니다. 이는 사용자가 매우 작은 위아래로 여러 번 움직이는 것과 같습니다. 따라서 사용자가 처음 터치한 위치를 기록해야 합니다. __startTouchTop . 손가락의 현재 위치에서 초기 트리거 위치를 빼면 사용자 __scrollTop이 이동한 거리가 됩니다. 구체적인 코드는 다음과 같습니다

let content = component.querySelector('[data-role=content]') // 内容容器
let __startTouchTop = 0 // 记录开始滚动的位置
let __scrollTop = 0 // 记录最终滚动的位置
// 这个方法下面马上讲解
let __callback = (top) => {
    const distance = top
    content.style.transform = 'translate3d(0, ' + distance + 'px, 0)'
}
// 这个方法下面马上讲解
let __publish = (top, animationDuration) => {
    __scrollTop = top
    __callback(top)
}
let touchStartHandler = (e) => {
    e.preventDefault()
    const target = e.touches ? e.touches[0] : e
    __startTouchTop = target.pageY
}
let touchMoveHandler = (e) => {
    const target = e.touches ? e.touches[0] : e
    let currentTouchTop = target.pageY
    let moveY = currentTouchTop - __startTouchTop
    let scrollTop = __scrollTop
    scrollTop = scrollTop + moveY
    __publish(scrollTop)
    __startTouchTop = currentTouchTop
}

참고 1: touchstart는 터치 위치를 기록해야 하며, touchend는 기록할 필요가 없습니다. 사용자의 첫 번째 터치 위치와 다음 터치 위치가 거의 같은 위치에 있을 가능성은 거의 없기 때문에 터치스타트에서 터치 위치를 재설정해야 합니다. 그렇지 않으면 사용자가 다시 터치할 때 콘텐츠가 깜박입니다.

** 참고 2: e.preventDefault() 메서드는 일부 브라우저의 호환성 문제를 처리하고 성능을 향상시킬 수 있습니다. 예를 들어, QQ 브라우저를 사용하여 손가락으로 아래로 당기면 브라우저 설명이 나타나서 메서드가 실패하게 됩니다. https://segmentfault.com/a/1190000014134234

https://www.cnblogs.com/ziyunfei/p/5545439.html**


위의 touchMoveHandler 메서드에 __callback 메서드가 나타납니다. 이 메서드는 콘텐츠 컨테이너의 위치를 ​​제어하는 ​​데 사용됩니다. __publish 메서드는 컨테이너의 위치를 ​​변경하기 위한 캡슐화 계층으로, 사용자의 손가락 움직임과 동기화할 수 있으며 사용자의 손가락 위치가 잘못된지 여부도 확인할 수 있습니다. 떠난 후. 현재는 사용자의 손가락 움직임을 따라가는 코드가 먼저 구현되어 있는데, 이 시점에서는 콘텐츠가 마우스 스크롤을 따라갈 수 있도록 브라우저를 모바일 모드로 조정할 수 있어야 합니다. 아래에서 하나씩 수정하겠습니다. 세 번째 단계는 손가락 슬라이딩의 최대값과 최소값을 제한하는 것입니다. 현재 사용자는 무한히 위아래로 당길 수 있는데 이는 분명히 잘못된 것입니다. 첫 번째 값이 선택한 실선을 약간 초과하면 끌어올릴 수 없습니다. 마지막 값이 선택한 실선을 약간 초과하면 끌어올릴 수 없습니다. 따라서 최소 스크롤 값: __minScrollTop과 최대 스크롤 값: __maxScrollTop

이라는 두 가지 값이 필요합니다. 계산 방법은 다음과 같아야 합니다. 사용자의 풀다운은 최대값을 생성하고 최대값은 첫 번째 요소는 가운데로 당겨집니다. 중간은 요소 컨테이너의 중앙에 위치해야 합니다

let __maxScrollTop = component.clientHeight / 2 // 滚动最大值

최소값은 사용자가 끌어 올렸을 때 마지막 요소가 중앙에 도달하는 위치이므로 콘텐츠 컨테이너-최대값이 되어야 합니다.

let __minScrollTop =  - (content.offsetHeight - __maxScrollTop) // 滚动最小值
이제 최대값과 최소값을 사용할 수 있으므로 손가락을 위아래로 당기는 과정에서 __scrollTop이 극값보다 크거나 작지 않은지 확인하기만 하면 되므로 다음 코드를 추가하세요. touchMoveHandler 함수

if (scrollTop > __maxScrollTop || scrollTop  __maxScrollTop) {
        scrollTop = __maxScrollTop
    } else {
        scrollTop = __minScrollTop
    }
}

第四步元素的位置准确卡在选中实线中

目前手指抬起的时候元素停留的位置是存在问题,这个也很容易理解。因为一个元素是有高度的,当你手指移动的距离只要不是元素高度的整数倍他就会卡在选中实线上。因此我们只需要对移动的距离除以元素的高度进行四舍五入取整之后再乘以元素的高度就能够保证元素位置是元素高得的倍数了

let indicator = component.querySelector('[data-role=indicator]')
let __itemHeight = parseFloat(window.getComputedStyle(indicator).height)

let touchEndHandler = () => { 
    let scrollTop = Math.round(__scrollTop / __itemHeight).toFixed(5) * __itemHeight
    __publish(scrollTop)
}

这样子产生了俩个问题,一是当极值四舍五入之后超越了极值就会出错,二是元素跳动太大用户体验不好。所以需要处理极值情况和添加动画滑动效果

处理上面问题中产生的极值问题

我们新建一个函数 __scrollTo 专门解决元素位置不对的问题

// 滚动到正确位置的方法
let __scrollTo = (top) => {
    top = Math.round((top / __itemHeight).toFixed(5)) * __itemHeight
    let newTop = Math.max(Math.min(__maxScrollTop, top), __minScrollTop)
    if (top !== newTop) {
        if (newTop >= __maxScrollTop) {
            top = newTop - __itemHeight / 2
        } else {
            top = newTop + __itemHeight / 2
        }
    }
    __publish(top, 250) // 这里传入了第二个参数动画时长,先留一个伏笔。后面会讲
}

简单分析一下,函数内第一行跟之前的一样。对位置进行四舍五入变成元素高度的倍数。第二行判断元素是否大于极值,如果大于最大值就取最大值,小于最小值就取最小值。当滚动值跟新的滚动值不一样的时候说明用户移动超过了极值。然后进行处理。大于等于最大值的时候元素的位置正好超出半个元素高度的,所以减掉高度的一半,小于最小值的时候恰好相反。添加一半

添加动画滑动效果

这个比较麻烦,关于动画效果是可以单独开一章来说的。这里我简单说一下我这个动画的思路吧。尽量长话短说。

首先讲解一下动画实现的原理,动画可以理解为多张连续的照片快速移动超过眼睛可以捕获的速度就会形成连贯的动作。这就是我理解的动画,像上面的 touchMoveHandler 方法其实是会被多次调用的,而且调用频率非常的高,高到了几毫秒调用一次,这个速度你肉眼肯定是分辨不出来的,而且每次移动的距离贼短。所以你看起来就有了跟随手指滚动的效果

所以当手指抬起的时候发现位置不正确这个时候应该实现一个滚动到正确位置的减速动画效果。这里我直接将 vux 里面的 animate.js 文件简化了一下直接拿过来用了

let running = {} // 运行
let counter = 1 // 计时器
let desiredFrames = 60 // 每秒多少帧
let millisecondsPerSecond = 1000 // 每秒的毫秒数

const Animate = {
  // 停止动画
  stop (id) {
    var cleared = running[id] != null
    if (cleared) {
      running[id] = null
    }
    return cleared
  },

  // 判断给定的动画是否还在运行
  isRunning (id) {
    return running[id] != null
  },
  start (stepCallback, verifyCallback, completedCallback, duration, easingMethod, root) {
    let start = Date.now()
    let percent = 0 // 百分比
    let id = counter++
    let dropCounter = 0

    let step = function () {
      let now = Date.now()

      if (!running[id] || (verifyCallback && !verifyCallback(id))) {
        running[id] = null
        completedCallback && completedCallback(desiredFrames - (dropCounter / ((now - start) / millisecondsPerSecond)), id, false)
        return
      }

      if (duration) {
        percent = (now - start) / duration
        if (percent > 1) {
          percent = 1
        }
      }
      let value = easingMethod ? easingMethod(percent) : percent
      if (percent !== 1 && ( !verifyCallback || verifyCallback(id))) {
        stepCallback(value)
        window.requestAnimationFrame(step)
      }
    }

    running[id] = true
    window.requestAnimationFrame(step)
    return id
  }
}

以上代码作为一个js外链单独引入,不知道取什么名就用 animate.js 好了。

简单讲解一下,主要是弄了一个叫 Animate 的对象,里面包含三个属性 stop, isRunning, start。 分别是停止动画,动画是否在执行,开始一个动画。start 是关键,因为其他俩个函数在这个项目中我都没有用过,哈哈。

start 函数包含很多个参数,stepCallback:每次动画执行的时候用户处理的界面元素滚动逻辑;verifyCallback:验证动画是否还需要进行的函数;completedCallback:动画完成时的回调函数;duration:动画时长;easingMethod:规定动画的运动方式,像快进慢出,快进快出等等;root:不用管了,没用到。

结束动画有俩种方式,第一种是传入的动画时长达成,另一种是验证动画是否还需要执行的函数验证通过。否则动画会一直运动

有了动画函数了,接下来就是如何使用了。这里我们补充一下 __publish 函数,并且添加一个是否开启动画的全局变量 __isAnimating 和 俩个曲线函数 easeOutCubic, easeInOutCubic

let __isAnimating = false // 是否开启动画
// 开始快后来慢的渐变曲线
let easeOutCubic = (pos) => {
    return (Math.pow((pos - 1), 3) + 1)
}
// 以满足开始和结束的动画
let easeInOutCubic = (pos) => {
    if ((pos /= 0.5)  {
    if (animationDuration) {
        let oldTop = __scrollTop
        let diffTop = top - oldTop
        let wasAnimating = __isAnimating
        let step = function (percent) {
            __scrollTop = oldTop + (diffTop * percent)
            __callback(__scrollTop)
        }
        let verify = function (id) {
            return __isAnimating === id
        }
        let completed = function (renderedFramesPerSecond, animationId, wasFinished) {
            if (animationId === __isAnimating) {
                __isAnimating = false
            }
        }
        __isAnimating = Animate.start(step, verify, completed, animationDuration, wasAnimating ? easeOutCubic : easeInOutCubic)
    } else {
        __scrollTop = top
        __callback(top)
    }
}

将上面的代码补充完整你就会发现滚动到正确位置的动画效果实现了,下面就讲讲实现的原理。

这里按照函数执行的顺序讲解吧。 首先是定义的几个变量, oldTop:用来保存元素的错误位置; diffTop: 传入的 top 是元素滚动的正确位置; step, verify, completed 是 Animate 对象需要的三个回调函数。里面的参数先不用管后面会讲,最下面给 __isAnimating 付了个值。 Animate.start 函数是有返回值的,返回值是当前动画的ID

其中需要注意 wasAnimating ? easeOutCubic : easeInOutCubic 这个。意思就是如果原来的动画存在就将 easeInOutCubic(俩头慢中间快的参数传入进去)函数传入进去, 如果不存在就传入进去 easeOutCubic(开始快后来慢)函数传入进去。符合的场景就是你手指快速滑动抬起动画会执行一段时间吧,这个过程动画就是从快到慢的过程,然后动画还没结束你又接着快速滑动是不是又从慢到快了。如果你不接着执行是不是动画就由快到慢结束了。这里为啥传入这俩个参数就不讲解了,完全可以再开一篇博客进行讲解比较麻烦。

step函数,接受一个 percent 翻译过来是百分比的意思。 下面的第一行代码

__scrollTop = oldTop + (diffTop * percent)

可以理解成, 老的位置 + 移动的距离 * 百分比 就是新的位置。百分比一直增大当百分比为百分之百的时候 __scrollTop === top。就实现了一个错误位置到正确位置的过度。

百分比的计算方式是根据时间来计算的,然后被动画曲线进行了加工

if (duration) {
    percent = (now - start) / duration
    if (percent > 1) {
      percent = 1
    }
}
let value = easingMethod ? easingMethod(percent) : percent

上面的是核心代码。start 是调用Animate.start属性的时候记录的一个当前时间,now是内部函数执行的时候记录的一个当前时间。 now - start 就是经过了多长时间,除以 duration动画时长就可以得出动画时长的百分比。下面判断 easingMethod 是否传入如果传入了就对本来匀速增加的百分比进行加工变成了动画曲线变化的百分比。

首先是 step 函数,每次运动调用的函数。接受了一个 percent ,翻译过来是百分比意思。 在外面我定了一个几个局部变量,分别是 oldTop: , , 正确位置减掉错误位置也就是元素滚动的距离。在 step 函数里赋予 __scrollTop 新值

step函数接受了一个叫百分比的参数。 用处就是当元素不在正确位置的时候会产生一个值 __scrollTop, 而元素应该的正确位置的值是 top,元素移动的距离就是 diffTop = top - oldTop 如何一步一步的移动到这个位置呢。就通过动画函数穿过来的这个百分比参数。这也是为啥在 __scrollTo 方法中调用 __publish 时加入第二个参数动画时长的原因了,这样就实现了一个自由滚动的动画

verify函数接受一个当前动画的id参数,验证规则就是 __isAnimating === id 时说明开启了下一个动画 __isAnimating 就会改变。导致验证失败,这个时候就会停止上一个动画

completed函数接受好几个参数,第一个参数是每秒多少帧,第二个参数是当前动画id,第三个参数是完成状态。这里主要用到了第二个参数当前动画id。动画完成的时候应该奖动画id变为false否则会一直走验证的逻辑。

第五步快速短暂触摸,让内容自己快速动起来

像目前内容滑动的距离基本是等于用户手指触摸的距离的,这样就跟实际使用不符合,实际中手指使劲一滑内容也会蹭蹭的滚动。就目前这个样子内容一多也能累死用户,所以需要添加用户使劲滑动内容快速滚动起来的逻辑

首先内容自己快速动起来很明显是有个触发条件的,这里的触发条件是 touchEndHandler 函数执行时的时间减去当最后一次执行 touchMoveHandler 函数的时间小于100毫秒。满足这种状态我们认为用户开启快速滚动状态。所以添加一个全局变量 __lastTouchMove 来记录最后一次执行 touchMoveHandler 函数的时间。

知道应该快速滚动了,如何判断应该滚动多长的距离呢?想一下当前的条件,有一个 __lastTouchMove 和执行 touchEndHandler 函数的时间。这俩个是不是能够的出来一个时间差。在想一下是不是有个 __scrollTop 滚动的位置,如果在获取到上一个滚动的位置是不是能够得到一个位置差。那位置 / 时间是等于速度的。我们让 __scrollTop + 速度 是不是可以得到新的位置。然后我们一直减小速度捡到最后等于 0 是不是就得到了滚动的位置,并且能够根据用户的快速滑动情况的出来应该滚动多长的距离,用户滑的越快速度越快距离越远,相反的用户滑动的速度越慢距离越近

遗憾的是在 touchEndHandler 函数中拿不到目标移动的距离 pageY。所以我们需要在 touchMoveHandler 方法中做手脚,去记录每次执行这个方法时的时间和位置。所以我们再添加一个全局变量 __positions 为数组类型。

// 上面提到的俩个全局变量的代码
let __lastTouchMove = 0 // 最后滚动时间记录
let __positions = [] // 记录位置和时间

然后我们将增加 __positions 的代码添加到 touchMoveHandler 方法中

if (__positions.length > 40) {
    __positions.splice(0, 20)
}
__positions.push(scrollTop, e.timeStamp)

__publish(scrollTop)

__startTouchTop = currentTouchTop
__lastTouchMove = e.timeStamp

其中如果 __positions 的长度超过40我们就取后20个。因为数组太大占用内存,而且循环遍历的时候还非常浪费时间。根据上面的逻辑我们手指快速移动不会取时间过长的数据,所以20足够了。当有了宝贵的位置和时间数据我们就需要在 touchEndHandler 方法中分析出来移动的速度了。这里我将完整的代码先切出来。

let __deceleratingMove = 0 // 减速状态每帧移动的距离
let __isDecelerating = false // 是否开启减速状态
let touchEndHandler = (e) => {
    if (e.timeStamp - __lastTouchMove  (self.__lastTouchMove - 100) 判断是从什么时候开始的快速滑动
        for (let i = endPos; i > 0 && positions[i] > (__lastTouchMove - 100); i -= 2) {
            startPos = i
        }
        if (startPos !== endPos) {
            // 计算这两点之间的相对运动
            let timeOffset = positions[endPos] - positions[startPos] // 快速开始时间 - 结束滚动时间
            let movedTop = __scrollTop - positions[startPos - 1] // 最终距离 - 快速开始距离
            
            __deceleratingMove = movedTop / timeOffset * (1000 / 60) // 1000 / 60 代表 1秒60每帧 也就是 60fps。玩游戏的可能理解 60fps是啥意思
    
            let minVelocityToStartDeceleration = 4 // 开始减速的最小速度 
            // 只有速度大于最小加速速度时才会出现下面的动画
            if (Math.abs(__deceleratingMove) > minVelocityToStartDeceleration) {
                __startDeceleration()
            }
        }
    }
    if (!__isDecelerating) {
        __scrollTo(__scrollTop)
    }
    
    __positions.length = 0
}

新添加了俩个全局变量运动速度和减速状态记录。当减速状态为true的时候肯定不能执行 __scrollTo 方法的因为这俩个方法是冲突的。所以需要 __isDecelerating 记录一下。里面新定义了一个函数 __startDeceleration。 我们的减速方法也主要是在这个方法里面实现的。给你一下代码

// 开始减速动画
let __startDeceleration = () => {
    let step = () => {
        let scrollTop = __scrollTop + __deceleratingMove
        let scrollTopFixed = Math.max(Math.min(__maxScrollTop, scrollTop), __minScrollTop) // 不小于最小值,不大于最大值
        if (scrollTopFixed !== scrollTop) {
            scrollTop = scrollTopFixed
            __deceleratingMove = 0
        }
        if (Math.abs(__deceleratingMove)  {
        // 保持减速运行需要多少速度
        let shouldContinue = Math.abs(__deceleratingMove) >= minVelocityToKeepDecelerating
        return shouldContinue
    }
    let completed = function (renderedFramesPerSecond, animationId, wasFinished) {
        __isDecelerating = false
        if (__scrollTop = __maxScrollTop) {
            __scrollTo(__scrollTop)
            return
        }
    }
    __isDecelerating = Animate.start(step, verify, completed)
}

当你把这些代码都加进去的时候,选择器插件基本上就已经完成了。下面讲解一下这段让你头痛的代码。

这里面用到了动画,所以肯定包含三大回调函数 step, verify, completed。然后一个一个讲解一下

step函数:这个函数是让内容一步一步运动的,这个函数基本上跟滚动到正确位置的函数相似度很高。 新的位置是老位置 __scrollTop 加上每帧移动的位置 __deceleratingMove。 然后让每帧移动的位置一直减少,但是需要注意 scrollTop 不能超出极值,所以做了最大值最小值判断当到达极值的时候就将 __deceleratingMove 赋值为0 。

if (Math.abs(__deceleratingMove) <p>这段代码,你可能佷懵。他的作用是当滚动的位置没有到达极值的时候如何让他卡在正确位置上。 Math.abs(__deceleratingMove) 这是每帧移动的距离的绝对值。当他小于1的时候说明移动的距离已经非常小了,用户基本上都察觉不到移动了。然后再用新位置对元素高度取余,如果余数为0表示正好卡在正确位置上,但是即使稍微比 0 大那么一丢丢也看不出来,而且基本不会那么巧取到 0,所以当余数满足小于 1 的时候讲每帧移动的距离赋值为0.</p><p>verify函数:定义了一个最小每帧移动距离的局部变量 minVelocityToKeepDecelerating, 当 __deceleratingMove 值小于他的时候说明用户基本上不会发现内容还在移动可以停下来了。</p><p>completed函数:既然是完成函数就一定要将 __isDecelerating 参数变为false,否则下次进行的不是快速移动内容就没法跑到正确位置上了。这里多加了一步是否是极值的判断,如果是极值就执行 __scrollTo 函数到正确位置上。</p><p>本篇文章到这里就已经全部结束了,更多其他精彩内容可以关注PHP中文网的<a href="http://www.php.cn/course/list/12.html" target="_blank">CSS视频教程</a>栏目!</p><p></p>

위 내용은 피커 플러그인 구현 방법 소개(코드)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 segmentfault.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제