vue-seamless-scroll從入坑到放棄
- 一、vue-seamless-scroll是什么?
- 二、使用步驟
- 1.引入庫
- 2.具體使用
- 三、下面就是我的踩坑之路
- 1.vue-seamless-scroll中的事件丟失問題
- 2. 解決事件丟失問題
- 3.vue-seamless-scroll 中異步資料的問題
- 4.資料時有時無解決
- 四、 vue-seamless-scroll 原始碼理解
- 1.頁面結構分析
- 2. js邏輯部分分析
- 2.1 屬性簡單介紹
- 2.2 computed屬性簡單介紹
- 2.3 methods方法介紹
- _initMove初始化方法原始碼分析(主要)
- _move滾動方法原始碼分析(主要)
- 五、 個人插件推薦
- 結語
- 參考質料
一、vue-seamless-scroll是什么?
vue-seamless-scroll是vue當前使用最多的一個串列回圈滾動插件,它功能強大使用方便,這也是我為什么選擇它來入坑的原因,
二、使用步驟
1.引入庫
//安裝vue-seamless-scroll插件
npm install vue-seamless-scroll --save
//在mian.js檔案中引入
import scroll from 'vue-seamless-scroll'
Vue.use(scroll)
2.具體使用
先貼上官方示例地址
//此處為官方示例地址的demo
<template>
<vue-seamless-scroll :data="listData" class="seamless-warp">
<ul class="item">
<li v-for="item in listData">
<span class="title" v-text="item.title"></span><span class="date" v-text="item.date"></span>
</li>
</ul>
</vue-seamless-scroll>
</template>
<style lang="scss" scoped>
.seamless-warp {
height: 229px;
overflow: hidden;
}
</style>
<script>
export default {
data () {
return {
listData: [{
'title': '無縫滾動第一行無縫滾動第一行',
'date': '2017-12-16'
}, {
'title': '無縫滾動第二行無縫滾動第二行',
'date': '2017-12-16'
}, {
'title': '無縫滾動第三行無縫滾動第三行',
'date': '2017-12-16'
}, {
'title': '無縫滾動第四行無縫滾動第四行',
'date': '2017-12-16'
}, {
'title': '無縫滾動第五行無縫滾動第五行',
'date': '2017-12-16'
}, {
'title': '無縫滾動第六行無縫滾動第六行',
'date': '2017-12-16'
}, {
'title': '無縫滾動第七行無縫滾動第七行',
'date': '2017-12-16'
}, {
'title': '無縫滾動第八行無縫滾動第八行',
'date': '2017-12-16'
}, {
'title': '無縫滾動第九行無縫滾動第九行',
'date': '2017-12-16'
}]
}
}
}
</script>
可以看出使用的方式沒什么難度,這里也貼出官網給出要注意的地方,
注意:需要給父容器一個height和:data='Array’和overfolw:hidden;左右滾動需要給ul容器一個初始化 css width,
如果以上步驟沒有你都沒有出錯就應該可以看到效果了,
三、下面就是我的踩坑之路
1.vue-seamless-scroll中的事件丟失問題
我們的業務邏輯大概是這樣的

剛開始我還以為可以開心的使用vue-seamless-scroll來做這個效果,但在實作這個滾動容器之后就發現了問題,這里面的點擊事件時有時無,

只能說我心態崩了,這是什么情況?
打開F12參看他的滾動容器,發現了他的一個驚天的秘密,

??原來他的實作方式是自己復制了一份相同的dom,貼在了原來dom的后面來實作回圈滾動的,看到這里我就猜到問題出現在他復制出來的dom中,又重新試了一下所有的點擊事件,果然有一個dom容器中的點擊事件完全沒有問題,而另一個中的點擊事件沒有任何相應,
碰到這個問題的同學我想肯定不會很少吧,估計都會自己去重新實作一個組件了吧,別急往下看,
2. 解決事件丟失問題
使用 @click="scrollClick($event)"的方式在外層父元素上添加點擊事件來獲取點擊的子dom的方式,再獲取其中的資料來進行下一步的事件處理,
<div
class="v-scroll"
v-if="isScroll(item.title)"
@click="scrollClick($event)">
<vue-seamless-scroll :data="newData" class="list-style ">
...
</vue-seamless-scroll>
</div>
父容器的事件處理
scrollClick(e) {
//通過 e.target獲取點擊的dom
//通過e.target.dataset獲取vuedom中的自定義屬性
let data = e.target.dataset;
this.$emit("supervise-detail", data.name, data.type);
}
在要監聽點擊事件的子dom中的處理
<span
:title="itemObj.number2"
:data-name="itemObj.name"
:data-type="'all'"
>/{{ itemObj.number2 }}</span
>
</span>
這里使用data-屬性的方式來進行屬性系結,以方便父容器的點擊事件中獲取該屬性,
這樣就可以通過其中的屬性來處理相應的事件內容了,
3.vue-seamless-scroll 中異步資料的問題
這個專案的業務邏輯是這樣的,

這個問題也是比較頭痛的,當時看到的效果是滾動容器中顯示的資料時有時無,
??原因是其中的資料可能是多個介面回傳回來的,所以vue-seamless-scroll的data更新達不到統一,所以在有些資料還沒有回傳時vue-seamless-scroll就將dom復制出來,而復制出來的dom中的資料不是回應式的,所有就會出現資料時有時無的情況,
這也使我想到了其中的解決辦法,接著往下看,
4.資料時有時無解決
-
1、使用promise、generator或async的方式來解決異步資料的問題
- 這里可以使用promise、generator或async的方式來解決異步的問題,使介面順序呼叫, 具體實作就不貼出來了,網上有很多,然后在最后一個介面資料成功回傳時,添加一個是否已全部加載的欄位,并為其賦值為true,以用來在vue-seamless-scroll上使用v-if來控制vdom的渲染, 2、使用key來使vue-seamless-scroll重新渲染
- 原理都是一樣的,就是通過vue-seamless-scroll的渲染來使其重新復制已經賦好值的dom,具體使用如下:
<vue-seamless-scroll ref="scrollModule" :data="newData" class="list-style " :key="scrollKey">
......
</vue-seamless-scroll>
在vue的data中添加一個scrollKey,然后系結在vue-seamless-scroll 的組件上,
methods: {
......
upDateKey() {
this.scrollKey++;
},
},
??然后在methods中添加一個upDateKey方法,使key值更新, 這時就可以在該組件中或在其父主鍵中使用$refs.設定的名稱.upDateKey()來修改key值,使其重新渲染,
關于key的使用不清楚的同學可以看一下下面的文章,
Vue 中 強制組件重新渲染的正確方法.
這時只需這介面資料成功回傳時呼叫其方法就可以了,
你以為這就完了?再來扒一扒它的原始碼吧
四、 vue-seamless-scroll 原始碼理解

??在已經引入的專案中,打開node_module庫就可以看到其原始碼了,結構也很簡單,主要的就index.js與myClass.vue檔案, index.js檔案是寫vue插件時候的一些配置,感興趣的可以搜一搜vue插件開發,主要的就是myClass.vue檔案,
1.頁面結構分析
查看如下:
<template>
<div ref="wrap">
<div :style="leftSwitch" v-if="navigation" :class="leftSwitchClass" @click="leftSwitchClick">
<slot name="left-switch"></slot>
</div>
<div :style="rightSwitch" v-if="navigation" :class="rightSwitchClass" @click="rightSwitchClick">
<slot name="right-switch"></slot>
</div>
<div
ref="realBox"
:style="pos"
@mouseenter="enter"
@mouseleave="leave"
@touchstart="touchStart"
@touchmove="touchMove"
@touchend="touchEnd"
>
<div ref="slotList" :style="float">
<slot></slot>
</div>
<div v-html="copyHtml" :style="float"></div>
</div>
</div>
</template>
??這是它全部的頁面結構代碼,是不是很簡潔,在這部分我們主要關注的就是ref="realBox"的div標簽,這個就是它實作回圈滾動的主要dom容器,可以看到這個dom容器上系結了很多事件,這個就是用來監聽pc端的滑鼠移入移出與移動端的手指觸摸事件,在繼續看他里面的結構,很明顯可以看到有一個包含slot標簽的dom容器,和一個帶有v-html="copyHtml"的dom容器,
??看到這里也初步的證明了我們前面的猜想,使用slot插入進來的dom就是資料擁有回應式監聽,事件保存完好的源dom(我們自己插入的dom),而使用v-html 復制出來的dom,就是資料沒有回應式,事件丟失的dom,OK繼續往下看,
2. js邏輯部分分析
2.1 屬性簡單介紹

上面這一部分都是一些基礎引數,根據圖上注釋理解即可,繼續走著,
2.2 computed屬性簡單介紹

接下來就是它的computed屬性,
??看第一個注釋從leftSwitchState到rightswitch都是在使用它的switch功能時用到的,這偏文章就不做解釋了,而且基本上都是一兩行代碼,注釋也寫的很清楚,所以感興趣的同學可以自行去研究,
接下來兩個就是比較重要的屬性:
??float屬性就是控制兩個滾動容器是水平排列還是垂直排列的動態樣式,
pos屬性,他就是控制滾動容器的滾動方向與速度的動態樣式,
ok繼續往下看

這些屬性就比較重要了下面來一個一個講
??defaultOption屬性就是滾動容器的默認配置,如果一些屬性沒用自行配置就會取其中默認的配置,
??options屬性就是將傳入的配置屬性與默認屬性進行合并,得到的最終配置引數的,
??navigation屬性是控制switch模式下的切換按鈕是否顯示的,
??autoPlay屬性是控制容器是否自動滾動的,這里代碼也很簡單,如果為switch模式(是否顯示switch的切換按鈕)默認不自動滾動,而不是swich模式就取傳入進來的值來判斷是否自動滾動,
繼續走著

這個就是剩下的computed屬性,上面的截圖中也做了一些簡單的解釋,取幾個主要的我在說一下,
??baseFontSize屬性是計算字體的基礎大小的,主要是用來為realSingleStopWidth與realSingleStopHeight屬性方便計算對應的寬度與高度,
??step屬性是計算滾動的步長(距離)的,在獲取自身的步長時,也將單步滾動的步長計算了看出來,這里作者也對應做出了相對應的警告,警告的內容就是上面的console.error中的內容,
??看到這里一些基本的屬性,就全部看完了,是不是沒什么太復雜的地方,好我們繼續往下看,我們下一步該考慮使用這個組件時他是從哪一步開始執行的,我想聰明的你很快就已經想到了,沒錯就是mounted鉤子函式,
2.3 methods方法介紹
那我們繼續走著

??這不要太簡單,我本來想只截下mounted中的代碼,可是只有一行,所以把methods中的方法也貼出來了,那我們就先大致了解一下(我們先猜測一下),
??leftSwitchClick 與 rightSwitchClick 方法看名字就能知道這是switch模式下,左邊按鈕與右邊按鈕的點擊事件,
??_cancle 方法的英文意思時快取,那我們就將它先理解為一些快取操作相關的方法,
??touchStart、touchMove、touchEnd 方法,我想看名字你們也應該猜到了這是移動端相關的手指觸摸事件,
??enter、leave方法,這兩個我們之前是不是在哪見過,是的它們就是滾動主容器上系結的滑鼠事件,忘記的可以回過頭去,在講解template結構上看一下,
??_move方法,名字是移動的意思,那我們就先將它理解為移動相關的函式,
??_initMove方法,這個就是我們剛才mounted中的初始化函式,我們等下就會詳細講解,
??_dataWarm方法,這個看意思是資料警告,如果展示還不理解先放一下,等下我們回頭再看,
??_startMove與_stopMove方法,這個也根據名字見他們風別理解為開始移動與停止移動的方法,
看到這里你可能會有這樣的感觸,原來命名方法或屬性的時候,名字取得與功能相對貼進一點是多么的友好!閑話少說,我們進入主要的部分,
_initMove初始化方法原始碼分析(主要)

??從全域來看,_initMove方法使用vue中的nextTick方法進行異步回呼處理,然后在器方法中獲取一些基礎的屬性,根據這些屬性進行一些滾動之前的初始化,
??先從第三四行代碼開始看,它呼叫了一下_dataWarm方法與清空copyHtml的操作,這里有遇到了這個_dataWarm方法,心急的同學可能會馬上去看一下里面的內容,不過我喜歡先讀完一整個方法在去看一些它使用過而我沒有了解的方法,那我們就繼續往下走,
第一個方框部分:
??這段代碼的意思是,如果為水平滾動這通過ref屬性來獲取整個容器的寬高與插入進來dom容器的寬度,代碼如下
this.height = this.$refs.wrap.offsetHeight
this.width = this.$refs.wrap.offsetWidth
let slotListWidth = this.$refs.slotList.offsetWidth
再根據autoPlay屬性來修正滾動容器的寬度與滾動父容器的寬度,最后再根據slotListWidth 設定內容的實際寬度realBoxWidth,
??第二個方框部分,是根據autoplay屬性來動態切換影片的樣式,
??第三個方框部分,是比較主要的,他通過scrollSwitch來判斷是否可以滾動,如果可以就重新開啟一個計時器,并將插入進來的dom使用$ref的方式獲取其中的dom保存下來,然后通過一個settimeout來實作異步獲取父滾動容器的高度,并呼叫滾動函式開始滾動,否則就會呼叫_cancle方法做一些快取處理,并初始化yPos與xPos屬性,
看到這里肯定有一些心急的同學把這個_cancle方法看完了,那我這里也回頭看下這個處理快取的函式吧,

這里面只有一行代碼,就是清除使用requestAnimationFrame方法所造成的快取,
那我們在回頭看一下_dataWarm方法,

看的出來作者還是很貼心的,接下來就是核心的_move方法了,
_move滾動方法原始碼分析(主要)
由于_move方法的代碼比較多,所以這里將逐層講解,這樣也可以更清晰的看出該部分的結構,便于我們更好的閱讀原始碼,

??這里先看最外層,作者標出的注釋也比較貼心,所以這里我們理解起來也很容易,他先使用了一個開關變數來模擬滑鼠的hover事件,實作停止與滾動的切換,接著就是滾動之前的快取清理操作,
??最后就是這個滾動函了數實作滾動的主邏輯,這里他使用了requestAnimationFrame方法來實作滾動的效果,在requestAnimationFrame中的這個回呼方法就是滾動的主函式,在這個函式后還使用bind方法將this保存了下來,以防回呼函式中的this指向問題,
??不得不提一下要注意使用requestAnimationFrame與settimeout來實作影片的區別了,具體的區別也是比較容易搜到的,我也提供了一下相關的質料,
setTimeout, setInterval 與 requestAnimationFrame 隱藏的各種坑

??這部分就是我們重點關注的物件,來看看這里可是怎么實作的,
??雖然其中的部分代碼沒有展開,但我們還是可以從注釋與定義的屬性名中猜到他們是做什么的,截圖中我也做了對應的注釋,函式的開始通過系結this來獲取滾動容器的真實寬度與高度,滾動的方向,單步停止等待時間、步長,在跟據這些屬性做出下面對應的一些邏輯操作(根據滾動方向與單步滾動模式下做出的一些對應的邏輯操作),

??先展開框出來的第一部分,截圖中我沒有添加注釋,因為這里也是比較簡單,我們先把帶有注釋的上與左一同理解,下與右一同理解,他們只有滾動變化的屬性(xPos與yPos)不同其他邏輯都是一模一樣的,那這里我就把帶有注釋上與下拿出來詳細解釋一下,
上:如果y軸方向滾動距離的絕對值(yPos的滾動距離)大于等于父容器高度的一半,就將y軸滾動的位置賦值為起始位置,再觸發外部的ScrollEnd方法,然后就是根據滾動步長來計算y軸滾動的位置,
下:如果y軸方向滾動距離的絕對值(yPos的滾動距離)小于等于滾動的起始位置時,就將y軸滾動的位置賦值為父容器高度的一半的負值,再觸發外部的ScrollEnd方法,然后就是根據滾動步長來計算y軸滾動的位置,

我們來根據這張圖理解一下,假設頁面剛好滾動到如圖這種情況,然后繼續向上滾動,剛好滾動到我們插入進來的dom完全隱藏時(剛好滾動了一個dom的高度),他就會將滾動的位置初始為起始的位置,如此往復,就達到了回圈滾動的效果,
向下滾動也可以想象一下滾動到當前位置時,我們插入進來的dom上面沒有dom了,所以就將位置向前了一個dom容器的高度,在如此往復,
接下來就是單步滾動模式下的邏輯了,

??這里也是非常簡單的,先將單步滾動的定時器清除掉,在進入判斷時寬度的單步滾動模式還是高度單步滾動模式,看判斷是寬度還是高度單步滾動的兩個if判斷中的內容,是不是一模一樣,所以不要怕干就完了,
??同樣的先判斷realSingleStopHeight或realSingleStopWidth是否存在,再通過滾動的距離對realSingleStopHeight或realSingleStopWidth取余,看是否小于步長,如果小于就進入單步定時器進行等待滾動,否則直接呼叫move方法進行滾動,是不是很簡單,
??到這里我們對vue-seamless-scroll中的核心原始碼就全部講解完畢了,如果有同學還對它其他部分的原始碼感興趣,可以根據這個分析思路繼續去了解一下,相信你們的能力一定可以的,
五、 個人插件推薦
??在這篇文章的最后我也帶上一點私貨,那就是我自己實作的一個回圈滾動插件,這個插件雖然沒有vue-seamless-scroll這個功能豐富,但他將vue-seamless-scroll不足的地方都彌補了一下,包括復制出來dom的點擊事件,及資料的回應依賴都保存了下來,還增加了滑鼠的滾輪模式,避免與滾動中的資料互動時的尷尬問題(例:滾動過去的資料要在等下一輪滾動才能交換),
插件地址:
https://www.npmjs.com/package/@david-j/vue-j-scroll
效果圖:

圖中滑鼠懸浮再滾動容器上仍何以使用滾輪控制滾動,如果想與滾動過去的某一條資料互動就不需要再等一個輪回了,是不是解決了某些同學的燃眉之急,如果感興趣并且使用了該插件,還請各位同學多多反饋,我會積極的對插件中的問題及時修復,功能上也會相對做出擴展的,
結語
好了這篇文章到這里就完全結束了,文章中我的一些個人看法較多,如果各位大佬們感覺有什么不足之處可盡情提出,我將及時的做出修改與答復,希望我們同學習,同進步,以后有時間我還會在寫一些其他方面的文章的,希望大家多多支持,
參考質料
vue-seamless-scroll使用中遇到關于click的問題.
Vue 中 強制組件重新渲染的正確方法.
setTimeout, setInterval 與 requestAnimationFrame 隱藏的各種坑
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/244681.html
標籤:其他
