中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

選擇器插件如何實現

發布時間:2020-10-16 15:48:04 來源:億速云 閱讀:150 作者:小新 欄目:web開發

這篇文章給大家分享的是有關選擇器插件如何實現的內容。小編覺得挺實用的,因此分享給大家做個參考。一起跟隨小編過來看看吧。

一個正常的選擇器插件是非常細致的,一步一步來描述就是。手指滑動內容跟隨手指滾動,當內容到底或觸頂的時候就不能在滾動并且內容要一直保持在正確的位置上。

第一步分析插件結構

首先要有一個插件容器,整個插件容器包含漸變背景,選中實線,內容容器。效果類似于下面:

選擇器插件如何實現

所以對應的代碼如下:

<div class="scroller-component" data-role="component">
    <div class="scroller-mask" data-role="mask"></div>
    <div class="scroller-indicator" data-role="indicator"></div>
    <div class="scroller-content" data-role="content">
        <div class="scroller-item" data-value='1'>1</div>
        <div class="scroller-item" data-value="2">2</div>
        <div class="scroller-item" data-value="3">3</div>
        <div class="scroller-item" data-value="4">4</div>
        <div class="scroller-item" data-value="5">5</div>
        <div class="scroller-item" data-value="6">6</div>
        <div class="scroller-item" data-value="7">7</div>
        <div class="scroller-item" data-value="8">8</div>
        <div class="scroller-item" data-value="9">9</div>
        <div class="scroller-item" data-value="10">10</div>
        <div class="scroller-item" data-value="11">11</div>
        <div class="scroller-item" data-value="12">12</div>
        <div class="scroller-item" data-value="13">13</div>
        <div class="scroller-item" data-value="14">14</div>
        <div class="scroller-item" data-value="15">15</div>
        <div class="scroller-item" data-value="16">16</div>
        <div class="scroller-item" data-value="17">17</div>
        <div class="scroller-item" data-value="18">18</div>
        <div class="scroller-item" data-value="19">19</div>
        <div class="scroller-item" data-value="20">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)

這樣當手指觸摸到 component 插件容器的時候就會觸發開始,移動,結束事件。

2.分析手指滑動容器移動效果

手指上滑內容上滑,手指下拉內容下拉。只需要控制 content 的位置改動的距離跟手指滑動的距離保持一致即可。這里用到了 transform 樣式的 translate3d(x, y, z) 屬性。其中 x, z 保持不變,y的值就是手指移動的值。

我們繼續做拆解,當手指下拉時 content 位置下移就會跟手勢保持一致。也就是 y 值變大(需要注意 y 軸正方向是往下的)。手指上拉正好與下拉上滑。當再次下拉或上拉時內容要在原來的基礎上保持不變。因此我們需要一個全局變量 __scrollTop 保存這個值。這個值等于用戶每次上拉下拉值的和,所以我們需要求出來用戶每次上拉下拉的值。

拆解用戶上拉的值,當用戶手觸摸到屏幕的時候就會觸發 touchstart 事件,移動的時候會觸發 touchmove 事件。離開的時候會觸發 touchend 事件。用戶上拉的初始值肯定是觸發 touchstart 時手指的位置。結束值就是 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 可以不記錄。因為用戶第一次觸摸的位置和下次觸摸的位置在同一個地方的可能性幾乎微乎其微,所以需要在 touchstart 里面重置觸摸位置。否則當用戶重新觸摸的時候內容會閃動

**注意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 < __minScrollTop) {
    if (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) < 1) {
        return 0.5 * Math.pow(pos, 3)
    }
    return 0.5 * (Math.pow((pos - 2), 3) + 2)
}

let __publish = (top, animationDuration) => {
    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 < 100) { // 如果抬起時間和最后移動時間小于 100 證明快速滾動過
        let positions = __positions
        let endPos = positions.length - 1
        let startPos = endPos
        // 由于保存的時候位置跟時間都保存了, 所以 i -= 2
        // positions[i] > (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) <= 1) {
            if (Math.abs(scrollTop % __itemHeight) < 1) {
                __deceleratingMove = 0
            }
        } else {
            __deceleratingMove *= 0.95
        }
        __publish(scrollTop)
    }
    let minVelocityToKeepDecelerating = 0.5
    let verify = () => {
        // 保持減速運行需要多少速度
        let shouldContinue = Math.abs(__deceleratingMove) >= minVelocityToKeepDecelerating
        return shouldContinue
    }
    let completed = function (renderedFramesPerSecond, animationId, wasFinished) {
        __isDecelerating = false
        if (__scrollTop <= __minScrollTop || __scrollTop >= __maxScrollTop) {
            __scrollTo(__scrollTop)
            return
        }
    }
    __isDecelerating = Animate.start(step, verify, completed)
}

當你把這些代碼都加進去的時候,選擇器插件基本上就已經完成了。下面講解一下這段讓你頭痛的代碼。

這里面用到了動畫,所以肯定包含三大回調函數 step, verify, completed。然后一個一個講解一下

step函數:這個函數是讓內容一步一步運動的,這個函數基本上跟滾動到正確位置的函數相似度很高。 新的位置是老位置 __scrollTop 加上每幀移動的位置 __deceleratingMove。 然后讓每幀移動的位置一直減少,但是需要注意 scrollTop 不能超出極值,所以做了最大值最小值判斷當到達極值的時候就將 __deceleratingMove 賦值為0 。

if (Math.abs(__deceleratingMove) <= 1) {
    if (Math.abs(scrollTop % __itemHeight) < 1) {
        __deceleratingMove = 0
    }
}

這段代碼,你可能佷懵。他的作用是當滾動的位置沒有到達極值的時候如何讓他卡在正確位置上。 Math.abs(__deceleratingMove) 這是每幀移動的距離的絕對值。當他小于1的時候說明移動的距離已經非常小了,用戶基本上都察覺不到移動了。然后再用新位置對元素高度取余,如果余數為0表示正好卡在正確位置上,但是即使稍微比 0 大那么一丟丟也看不出來,而且基本不會那么巧取到 0,所以當余數滿足小于 1 的時候講每幀移動的距離賦值為0.

verify函數:定義了一個最小每幀移動距離的局部變量 minVelocityToKeepDecelerating, 當 __deceleratingMove 值小于他的時候說明用戶基本上不會發現內容還在移動可以停下來了。

completed函數:既然是完成函數就一定要將 __isDecelerating 參數變為false,否則下次進行的不是快速移動內容就沒法跑到正確位置上了。這里多加了一步是否是極值的判斷,如果是極值就執行 __scrollTo 函數到正確位置上。

感謝各位的閱讀!關于選擇器插件如何實現就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

许昌市| 尼木县| 揭阳市| 册亨县| 鸡西市| 犍为县| 兴文县| 金溪县| 新余市| 洞头县| 红桥区| 陇南市| 绥宁县| 迭部县| 青神县| 集安市| 麻栗坡县| 沈阳市| 蒙山县| 门头沟区| 五大连池市| 旬阳县| 马山县| 巩留县| 张家川| 丹棱县| 乌什县| 屏东市| 景东| 榆中县| 根河市| 普洱| 奎屯市| 彭山县| 宝鸡市| 文山县| 沾化县| 高阳县| 乃东县| 浙江省| 富宁县|