Heim  >  Artikel  >  Web-Frontend  >  Einführung in die Implementierungsmethode des Picker-Plug-Ins (Code)

Einführung in die Implementierungsmethode des Picker-Plug-Ins (Code)

不言
不言nach vorne
2019-03-30 11:32:183165Durchsuche

Der Inhalt dieses Artikels befasst sich mit der Implementierungsmethode (Code) des Picker-Plug-Ins. Ich hoffe, dass er für Freunde hilfreich ist.

Ein normales Selector-Plugin ist sehr detailliert und kann Schritt für Schritt beschrieben werden. Wenn der Finger gleitet, scrollt der Inhalt mit dem Finger. Wenn der Inhalt unten oder oben ankommt, kann nicht gescrollt werden und der Inhalt muss immer an der richtigen Position bleiben.

Der erste Schritt besteht darin, die Plug-In-Struktur zu analysieren.

Zunächst muss ein Plug-In-Container vorhanden sein. Wählen Sie den Volumenkörper aus Zeile und den Inhaltscontainer. Der Effekt ähnelt dem folgenden:

Einführung in die Implementierungsmethode des Picker-Plug-Ins (Code)

Der entsprechende Code lautet also wie folgt:

<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;
}

Der CSS-Code wird hauptsächlich als Stilanzeige verwendet und ist über externe Links eingeführt. Ich werde hier nicht zu viel erklären.

Der zweite Schritt besteht darin, den Finger-Scroll-Container zu implementieren

1. Fingerberührungsereignis hinzufügen

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)

Auf diese Weise wird der Komponenten-Plug-in-Container berührt, wenn der Finger ihn berührt Start-, Bewegungs- und Endereignis werden ausgelöst.

2. Analysieren Sie die Auswirkung der Fingerbewegung des Containers.

Wischen Sie mit Ihrem Finger nach oben, um den Inhalt nach oben zu schieben, und ziehen Sie Ihren Finger nach unten, um den Inhalt nach unten zu ziehen. Sie müssen nur den Abstand steuern, in dem sich die Position des Inhalts ändert, um mit dem Abstand übereinzustimmen, in dem Ihr Finger gleitet. Hier wird das Attribut translator3d(x, y, z) des Transformationsstils verwendet. Unter diesen bleiben x und z unverändert, und der Wert von y ist der Wert der Fingerbewegung.

Fahren wir mit der Demontage fort. Wenn der Finger nach unten gezogen wird, verschiebt sich die Inhaltsposition nach unten, um mit der Geste übereinzustimmen. Das heißt, der y-Wert wird größer (beachten Sie, dass die positive Richtung der y-Achse nach unten zeigt). Der Finger zieht gerade noch rechtzeitig nach oben, um nach oben zu gleiten. Beim Herunterziehen oder erneuten Hochziehen sollte der Inhalt auf der ursprünglichen Basis unverändert bleiben. Daher benötigen wir eine globale Variable __scrollTop, um diesen Wert zu speichern. Dieser Wert entspricht jedes Mal der Summe der Pull-Up- und Pull-Down-Werte des Benutzers, daher müssen wir jedes Mal den Pull-Up- und Pull-Down-Wert des Benutzers ermitteln.

Zerlegen Sie den vom Benutzer aufgerufenen Wert. Das Touchstart-Ereignis wird ausgelöst, wenn der Benutzer den Bildschirm berührt, und das Touchmove-Ereignis wird ausgelöst, wenn sich der Benutzer bewegt. Beim Verlassen wird das Touchend-Ereignis ausgelöst. Der Anfangswert des Pull-Ups des Benutzers muss die Position des Fingers sein, wenn Touchstart ausgelöst wird. Der Endwert ist die Fingerposition am Touchend. Auf diese Weise kann der Inhalt jedoch nicht der Echtzeitbewegung des Fingers folgen. Daher ist es notwendig, das Touchmove-Ereignis zu zerlegen

Das Touchmove-Ereignis wird kontinuierlich ausgelöst, wenn sich der Finger des Benutzers bewegt, was den mehreren extrem kleinen Auf- und Abbewegungen des Benutzers entspricht. Daher müssen wir den Ort aufzeichnen, den der Benutzer zum ersten Mal berührt hat. __startTouchTop . Das Subtrahieren der anfänglichen Triggerposition von der aktuellen Position des Fingers ergibt die vom Benutzer zurückgelegte Strecke __scrollTop. Der spezifische Code lautet wie folgt

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
}

Hinweis 1: Touchstart muss die Berührungsposition aufzeichnen, Touchend muss nicht aufzeichnen. Da die Wahrscheinlichkeit, dass sich die erste Berührungsposition des Benutzers und die nächste Berührungsposition fast an derselben Stelle befinden, nahezu gering ist, muss die Berührungsposition in Touchstart zurückgesetzt werden. Andernfalls blinkt der Inhalt, wenn der Benutzer ihn erneut berührt

**Hinweis 2: Die e.preventDefault()-Methode dient zur Behebung von Kompatibilitätsproblemen einiger Browser und kann die Leistung verbessern. Wenn Sie beispielsweise den QQ-Browser zum Herunterziehen mit dem Finger verwenden, wird die Browserbeschreibung angezeigt, was dazu führt, dass die Methode fehlschlägt. Sie können auf das Dokument verweisen https://segmentfault.com/a/1190000014134234
https://www.cnblogs.com/ziyunfei/p/5545439.html**

Erschien in der touchMoveHandler-Methode oben_ _callback-Methode. Diese Methode wird verwendet, um die Position des Inhaltscontainers zu steuern. Die __publish-Methode ist eine Kapselungsebene zum Ändern der Position des Containers. Sie kann mit den Fingerbewegungen des Benutzers synchronisiert werden und auch feststellen, ob die Position des Fingers des Benutzers falsch ist nachdem man es verlassen hat. Derzeit ist der Code zum Verfolgen der Fingerbewegung des Benutzers

hier. Wenn Sie den Browser verwenden, um auf den Mobilmodus umzustellen, sollten Sie in der Lage sein, den Inhalt mit der Maus scrollen zu lassen, aber das ist der Fall Es gibt immer noch viele Probleme, die im Folgenden besprochen werden.

Der dritte Schritt besteht darin, die maximalen und minimalen Werte des Fingergleitens zu begrenzen.

Derzeit können Benutzer dies tun unendlich hoch und runter ziehen, was offensichtlich falsch ist. Wenn der erste Wert die ausgewählte durchgezogene Linie geringfügig überschreitet, kann er nicht nach unten gezogen werden. Wenn der letzte Wert die ausgewählte durchgezogene Linie geringfügig überschreitet, kann er nicht nach oben gezogen werden. Wir benötigen also zwei Werte, den minimalen Scroll-Wert: __minScrollTop und den maximalen Scroll-Wert: __maxScrollTop

Die Berechnungsmethode sollte wie folgt aussehen: Das Pulldown-Menü des Benutzers erzeugt einen Maximalwert, und der Maximalwert sollte sein Das erste Element wurde zur mittleren Position heruntergezogen. Die Mitte sollte die Position in der Mitte des Elementcontainers sein

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

Der Mindestwert sollte die Position sein, an der das letzte Element die Mitte erreicht, wenn der Benutzer nach oben zieht, also sollte es der Maximalwert des Inhaltscontainers sein .

let __minScrollTop =  - (content.offsetHeight - __maxScrollTop) // 滚动最小值

Da nun die Maximal- und Minimalwerte verfügbar sind, müssen Sie nur noch sicherstellen, dass __scrollTop beim Auf- und Abziehen Ihres Fingers nicht größer oder kleiner als der Extremwert ist, also hinzufügen Fügen Sie der touchMoveHandler-Funktion den folgenden Code hinzu

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>

Das obige ist der detaillierte Inhalt vonEinführung in die Implementierungsmethode des Picker-Plug-Ins (Code). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:segmentfault.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen