封裝一個本身的輪播圖組件(基於 Vue)

建立目錄結構

vue-swiper/
├── src/
│   ├── components/ //內置組件
│   │   ├── indicator.vue  // 指示器組件
│   │   └── item.vue  // 單個輪播圖容器組件
│   ├── main.js  // 項目出口
│   └── main.vue  //組件出口
├── README.md  
├── package.json
└── vue.config.js  // 組件打包配置文件

項目結構解析

咱們知道一個輪播圖是由其容器和內容構成的,這裏首先把整個輪播圖組件拆分爲入口組件和其要用到的子組件(指示器組件能夠根據本身的項目維護方式自由拆分,這裏把它單獨拆出來便於之後維護)。以往咱們在使用第三方組件的時候可能會看到其組件就是單獨的一個入口,全部的屬性傳入和事件監聽都是放在了一個大的組件上了,其實這種封裝的方式後期是很差維護的,並且在使用的時候也是不容易發現出現的問題,爲了能讓組件自身更具備語義化以及開發當中便於調試和後期組件的維護,咱們把容器和內容分離開拆成兩個組件,這樣用戶在模板中書寫組件時候就能像使用普通的 HTML 標籤同樣嵌套使用了。javascript

前提

以往咱們開發一個頁面中的輪播圖可能會牽扯到大量手動的 DOM 樣式操做,這裏帶來的問題就是咱們關心的視圖變化和邏輯操做混到了一塊兒,關注點沒有分離開,不管從維護仍是代碼的可讀性這種方式都不是最優的。如今咱們是基於 Vue 開發的這個組件,這樣就能夠利用它最核心的思想(數據驅動視圖改變)開完成這個組件的開發。vue

數據驅動動畫

有了數據驅動的這個主要思想,咱們就能夠圍繞它展開組件開發了。首先把輪播圖播放動畫當中能用到的狀態變量進行初始化java

// main.vue
data(){
  return {
    reversing:false,//控制動畫播放到首尾時無縫跳轉的開關
    swiperItemCount:0,// 初始化傳入的輪播圖個數
    index:0 // 控制輪播圖當前位置的索引
  }
},
computed:{
  scrollItemCount(){  // 內容實際存在的圖片個數
    return this.swiperItemCount+2 // 組件初始化之後須要複製傳進來的首尾兩張圖片到指定位置,因此這裏須要加上2
  }
}

之因此要定義 reversing 這個變量是由於當動畫播放到首尾端點的時候,咱們要瞬間跳轉到對應的首尾位置,而後就能夠更改這個變量的值來關閉相應的動畫以達到用戶視覺上無縫滾動的效果。node

至於 index 其語義大概已經描述了它所須要作的事情了,就是經過更改這個索引的值以驅動圖片的位置移動,這樣就有了視覺上動畫的效果。相關的代碼以下git

watch: {
    index(newIndex, oldIndex) {
      const endIndex = this.scrollItemCount - 1
      if (newIndex === endIndex && newIndex > oldIndex) {
        setTimeout(() => {
          this.reversing = true
          this.index = 1
          setTimeout(() => {
            this.reversing = false
          }, 100)
        }, this.duration)
      } else if (newIndex === 0 && newIndex < oldIndex) {
        setTimeout(() => {
          this.reversing = true
          this.index = endIndex - 1
          setTimeout(() => {
            this.reversing = false
          }, 100)
        }, this.duration)
      }
    },
 }

這裏經過觀測動畫播放的當前位置這個變量,咱們在相應的時機更改它的值來達到整個包裝容器的瞬間移動,這樣也就產生了圖片播放連續的動畫效果了。github

相關的技術要點

前提

單個的圖片內容是以 slot(相關 Api 查看這裏,本片文章不作介紹)的方式接收的,咱們知道當前組件的$slot 屬性存儲的是 vnode(不瞭解 vnode 的看這裏,一樣不過多介紹)。json

匿名插槽內容篩選

有了 slot,組件內部能夠接受外界傳進來的一切內容,而咱們這裏只須要組件定義的指定子組件,因此在組件啓動後還須要對默認的匿名插槽從新處理後纔可使用,讓咱們看代碼吧api

created() {
    this.$slots.default = this.$slots.default.filter(vnode => {
      return (
        vnode.componentOptions && vnode.componentOptions.tag === 'swiper-item'
      )

      // swiper-item 取決於註冊的指定組件名稱
    })
    this.swiperItemCount = this.$slots.default.length
  },

瞭解 Vue 虛擬 DOM 渲染原理的同窗應該知道,每一次的 vnode 更新都會致使頁面組件的衝渲染,代碼中經過過濾須要的 vnode 從新賦值到接收匿名插槽的接口上,這樣 Vue 內部經過檢測 vnode 的變動會渲染新的 vnode 到組件視圖上,接下來就能夠調整內容節點的結構了。app

如何複製外界傳入的首尾圖片

vnode 有一個 tag 屬性存儲了它所渲染的真實 DOM 的引用,這樣咱們就能夠經過相關的 DOM 操做 Api 來複制這些節點從而達到咱們的目的,相關的代碼片斷在這裏動畫

mounted() {
  const firstItem = this.$slots.default[0].elm
  const lastItem = this.$slots.default[this.$slots.default.length - 1].elm
  this.$refs.wrapper.appendChild(firstItem.cloneNode(true))
  this.$refs.wrapper.insertBefore(lastItem.cloneNode(true), firstItem)
  this.autoplay && this.play()
},

組件效果展現

good-swiper

總結

文章經過介紹 Vue 數據驅動的思想實現了一些動畫效果,當咱們想封裝一些別的組件的時候一樣能夠利用這一點來達到各類各樣的需求。
至於組件其餘功能的具體實現過程這裏就不在介紹了,有興趣的同窗能夠查看本項目的 Github 倉庫:good-swiper

備註:本篇文章屬於做者原創,轉載請標註出處,謝謝!

相關文章
相關標籤/搜索