vue-seamless-scroll 從入坑到放棄

一、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中的事件丟失問題

我們的業務邏輯大概是這樣的
![在這裏插入圖片描述](https://img-blog.csdnimg.cn/20201204095345565.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzk5Mjg2Nw==,size_16,color_FFFFFF,t_70
剛開始我還以爲可以開心的使用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 隱藏的各種坑

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章