天天看點

仿Element-UI元件封裝系列6-輪播圖元件

目标:

  • 實作一個輪播圖元件
    仿Element-UI元件封裝系列6-輪播圖元件

基本步驟:

  • 開發元件。packages/slider/slider.vue
  • 添加到packages/index.js,全局注冊
  • 在測試頁面中使用元件

準備工作

  1. 添加元件

    建立一個元件

    仿Element-UI元件封裝系列6-輪播圖元件
    <template>
      <div class=''>
        我是一個輪播圖元件
      </div>
    </template>
    
    <script>
    export default {
      name: 'MySlider'
    }
    </script>
    
               
  2. 導出元件

    packages/index.js

    // semantic-ui樣式
    import 'semantic-ui-css/semantic.css'
    
    // 收集所有packages下面的元件,并按vue插件的格式做導出
    import Button from './button/button.vue'
    import Slider from './slider/slider.vue'
    
    export default {
      install (Vue) {
        // 建立全局元件
        Vue.component('MyButton', Button)
        Vue.component('MySlider', Slider)
      }
    }
    
               
  3. 在測試項目中使用元件

    (1)添加測試頁面

仿Element-UI元件封裝系列6-輪播圖元件

内容

<template>
  <div class="home">
    <h3>對輪播圖元件進行測試</h3>
    <!-- 2000表示 每隔2s自動播放下一張 -->
    <my-slider style="width:250px;height:350px;"
    :auto="2000"
    :curIdx="curIdx"
    :list="list"></my-slider>
  </div>
</template>

<script>

export default {
  name: 'SliderTest',
  data () {
    return {
      curIdx: 1, // 預設顯示第二張圖
      list: [ // 需要輪播的圖檔資料
     {
       url: 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1603552928895&di=033a3b0900cb6094b581325f1b6a43ad&imgtype=0&src=http%3A%2F%2Fpic1.win4000.com%2Fpic%2Fe%2F0f%2F1b7e513753.jpg',
       alt: '離太陽最近的地方的一場春耕'
     },
     {
       url: 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1603552961721&di=e0ff498eb7dbb671ea204454cf0ca58f&imgtype=0&src=http%3A%2F%2Fhbimg.b0.upaiyun.com%2F2f6ae348b19c83fdc721ca5a54d4adb8d7455fa31dc76-GMqiCq_fw658',
       alt: '長江三峽水庫'
     },
     {
       url: 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1603553792310&di=db02409d236f067161d3a6d51439d3a1&imgtype=0&src=http%3A%2F%2Fpic1.win4000.com%2Fpic%2F1%2F74%2Fe433515514.jpg_195.jpg',
       alt: '月球方舟在格陵蘭島測試'
     },
     {
       url: 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1603553028011&di=100b009e9f826b13b7d52b86a1947568&imgtype=0&src=http%3A%2F%2Fpic1.win4000.com%2Fpic%2F1%2F50%2F3b06419346.jpg',
       alt: '虎門大橋水域恢複通航'
     }
   ]
 }
}
</script>
           

(2)添加路由

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/button',
    name: 'Button',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/Button.vue')
  },
  {
    path: '/slider',
    name: 'Slider',
    component: () => import('../views/SliderTest.vue')
  }
]
           

(3)添加路由導航

App.vue

<div id="app">
    <div id="nav">
      <router-link to="/">按鈕</router-link> |
      <router-link to="/test-alert">警示框</router-link> |
      <router-link to="/my-dialog">彈出框</router-link> |
      <router-link to="/my-switch">開關</router-link> |
      <router-link to="/slider">輪播圖</router-link> |
      <router-link to="/headline">标題元件</router-link>
    </div>
    <router-view/>
  </div>
           

效果

仿Element-UI元件封裝系列6-輪播圖元件

slider元件的基本結構和樣式

下面是一個結構和樣式,修改slider.vue的内容如下:

<template>
  <div class="slider">
    <div class="slider-content">
      <!-- 每一個slider-item表示一張圖 -->
      <div class="slider-item">
        <img src="http://contentcms-bj.cdn.bcebos.com/cmspic/870d7d4f1549ffccb64a420acb848063.jpeg?x-bce-process=image/crop,x_0,y_0,w_899,h_490" alt="離太陽最近的地方的一場春耕" />
      </div>

    </div>

    <!-- 左右兩個按鈕 -->
    <span class="btn btn_left"></span>
    <span class="btn btn_right"></span>

    <!-- 方案 -->
    <div class="txt">離太陽最近的地方的一場春耕</div>

    <!-- 訓示條 -->
    <ol class="indirector">
      <li></li>
      <li class="current"></li>
      <li></li>
    </ol>
  </div>
</template>
<style>
.slider .slider-content,
.slider img {
  width: 100%;
  height: 100%;
}
.slider {
  margin: 0 auto;
  border: 1px solid #ccc;
  position: relative;
  cursor: pointer;
}
.slider .slider-content {
  overflow: hidden;
  position: relative;
}
.slider .slider-content .slider-item {
  top: 0;
  left: 0;
  width:100%;
  height:100%;
}
.slider img {
  width: 100%;
}
.slider .btn,
.slider .txt,
.slider .indirector {
  position: absolute;
}
.slider .btn {
  cursor: pointer;
  width: 50px;
  height: 50px;
  border-radius: 50%;
  background-color: rgba(255, 255, 255, 0);
  top: 50%;
  transform: translateY(-50%);
  transition: background-color 0.2s;
}
.slider .btn:hover {
  background-color: rgba(255, 255, 255, 1);
}

.slider .btn:before,
.slider .btn:after {
  content: "";
  height: 3px;
  width: 25px;
  background-color: #fff;
  position: absolute;
  left: 15px;
  top: 23px;
  transform: rotateZ(60deg);
  transform-origin: 0px center;
  transition: all 0.2s;
}
.slider .btn:after {
  transform: rotateZ(-60deg);
}
.slider .btn:hover:before {
  transform: rotateZ(45deg);
  background-color: red;
}
.slider .btn:hover:after {
  transform: rotateZ(-45deg);
  background-color: red;
}

.slider .btn.btn_right:before,
.slider .btn.btn_right:after {
  transform-origin: right center;
}

.slider .btn.btn_left {
  left: 20px;
}
.slider .btn.btn_right {
  right: 20px;
}

.slider .txt {
  text-indent: 1em;
  line-height: 40px;
  background-color: rgba(0, 0, 0, 0.5);
  text-align: left;
  bottom: 0;
  left: 0;
  width: 100%;
  color: #fff;
}
.slider .indirector {
  bottom: 10px;
  right: 1em;
  margin: 0;
}
.slider .indirector li {
  display: inline-block;
  margin: 0 5px;
  height: 10px;
  width: 10px;
  border-radius: 50%;
  background-color: #fff;
}
.slider .indirector li {
  transition: transform 0.2s;
}
.slider .indirector .current {
  background-color: #369;
  transform: scale(1.2);
}
</style>
           

分析

功能

  • 允許使用者傳入圖檔清單
  • 左右按鈕可以點選切換
  • 指定從哪一張開始放映
  • 自動播放功能(指定播放時間間隔,滑鼠進入時,要停止自動播放)
  • 訓示條上 滑鼠滑動 也能切換

測試用例

<template>
  <div class="about">
    <h1>對輪播元件測試</h1>
    <!--
      這裡的style會在元件的根元素上生效
    - list:是圖檔清單資料,它是一個數組,儲存要輪播圖檔資訊
    - auto:2000. 開啟自動播放。2000毫秒切換一張.
    - curIdx:初始播放第幾張
-->
    <my-slider
      style="width:550px;height:350px;"
      auto='2000'
      curIdx='1'
      :list="list">
    </my-slider>
  </div>
</template>
           
  • list:是圖檔清單資料,它是一個數組,儲存要輪播圖檔資訊
  • auto:2000. 開啟自動播放。2000毫秒切換一張.
  • curIdx:初始播放第幾張

基本實作

目标:

  • 從用prop傳入三項:
    • 圖檔清單
    • 目前索引
    • auto

思路:

  • 用v-for循環生成圖檔清單: 隻顯示目前項,其它隐藏
  • 顯示目前圖檔的文案
  • 用v-for循環生成訓示條,隻有目前項是高亮的

$attrs和props

在父元件中向子元件傳遞自定義屬性時,如果:

  • 在子元件内部有對應的props項, 則,資料會傳入props。
  • 在子元件中沒有定義對應的props,則在元件内部可以通過 $attrs來檢視

$attrs最終會顯示在dom格式上

<div data-v-63407e8a=""
class="slider" 
list="[object Object],[object Object],[object Object],[object Object]" abc="123" 
style="width: 550px; height: 350px;">
           

元件代碼

  • 通過循環生成輪播圖的每一項,根據下标決定顯示與否
  • 根據list及傳入的下标,取出目前的方案
  • 通過循環生成訓示條的每一項,根據下标決定顯示高亮
<template>
  <div class="slider">
    <div class="slider-content">
      <!-- 每一個slider-item表示一張圖 -->
      <div class="slider-item"
        v-for="(item, idx) in list"
        :key="idx"
        v-show="curIdx==idx">
        <img :src="item.url" :alt="item.alt" />
      </div>

    </div>

    <!-- 左右兩個按鈕 -->
    <span class="btn btn_left"></span>
    <span class="btn btn_right"></span>

    <!-- 文案 -->
    <div class="txt">{{list[curIdx].alt}}</div>

    <!-- 訓示條 -->
    <ol class="indirector">
      <li v-for="(item, idx) in list"
        :key="idx"
        :class="{'current': curIdx==idx}"
        ></li>
    </ol>
  </div>
</template>
<script>
export default {
  name: 'MySlider',
  // 在父元件中向子元件傳遞自定義屬性時,如果在子元件内部有對應的props項,
  // 則,資料會傳入props。如果在子元件中沒有定義對應的props,則在元件内部可以通過 $attrs來檢視
  props: {
    list: {
      type: Array,
      required: true
    },
    curIdx: {
      type: [String, Number],
      default: 0 // 預設顯示第一張
    },
    auto: {
      type: [String, Number], // "2000", 2000
      default: 0
    }
  }
}
</script>
           

給按鈕添加點選事件

目标:讓輪播圖上的左右按鈕産生點選切換效果。

思路:

  • 修改目前項的下标
  • 添加下标索值即可。要注意越界的情況。

步驟:

  • 添加一個資料項:currentIndex。由于在切換上一張下一張時本質上是要修改索引值,而這個索引值是通過prop傳入的,我們不能直接修改,是以這裡補充一個currentIndex來儲存傳入的索引值。
    data () {
        return {
          // 由于在子元件内,不允許直接指派給props
          // 在這裡,補充一個資料項,從curIdx中擷取初始值
          currentIndex: this.curIdx
        }
      }
               
  • 把模闆中的原來對curIdx的引用,改成對currentIndex的引用。
  • 在模闆中對按鈕添加點選事件,在事件中修改currentIndex的值。

模闆

<template>
  <div class="slider">
    ....

    <!-- 左右兩個按鈕 -->
+    <span class="btn btn_left" @click="hPrev"></span>
+    <span class="btn btn_right" @click="hNext"></span>

    ....
  </div>
</template>
           

代碼

methods: {
    hNext () {
      // 下一張圖
      // 1. 把下标向後移
      // 2. 判斷是否越界
      this.currentIndex++
      if (this.currentIndex === this.list.length) {
        this.currentIndex = 0
      }
    },
    hPrev () {
      // 上一張圖
      // 1. 把下标向前移
      // 2. 判斷是否越界
      this.currentIndex--
      if (this.currentIndex === -1) {
        this.currentIndex = this.list.length - 1
      }
      // this.currentIndex = this.currentIndex === -1 ? this.list.length - 1 : this.currentIndex
    }
  }
           

添加自動播放功能

仿Element-UI元件封裝系列6-輪播圖元件

目标:

  • 允許使用者手動啟動輪播功能

思路:

  • 在元件上添加

    auto

    這個prop,它用來接收使用者的輸入,如果有值,則開啟定時器
  • 在建立元件時,如果有auto的值,就用setInterval來啟動定時器。

元件代碼:

補充一個鈎子函數created

created () {
    // 如果使用者設定了auto值,則表示開啟自動播放功能
    // 每隔auto值,去調用一次:播放下一張
    if (this.auto) {
      setInterval(() => {
        this.hNext()
      }, this.auto)
    }
  }
           

補充一下對auto屬性值的限制

props: {
    // ...
    auto: {
      type: [String, Number], // "2000", 2000
      default: 0,
+      validator: function (val) {
        if (typeof val === 'number') {
          return val >= 0
        }
        if (typeof val === 'string') {
          if (isNaN(val)) {
            console.log(val, '不是合法的數值')
            return false
          } else {
            return true
          }
        }
      }
    }
  },
           

改進輪播功能:hover暫停

目标:

  • 當使用者滑鼠進入輪播圖時,停止動畫(clearInterval)

    當使用者滑鼠移出輪播圖時,再次啟動動畫

思路:

  • 在元件中,給外層容器添加mouseenter,mouseleave事件。并在各自事件中删除或啟動定時器。
<div class="slider"
  @mouseleave="hMouseLeave"
  @mouseenter="hMouseEnter">
     //...
</div>
           

代碼

補充一個資料項,儲存定時器

data () {
    return {
      // 由于在子元件内部,不允許修改從父元件中傳入的props
      // 是以,這裡補充定義一個資料項,從curIdx中擷取初始值
      currentIdx: this.curIdx,
+      timer: null // 儲存定時器 (它不是必須的,隻是讓大家看到值)
    }
  }
           

方法

created () {
    this.play()
  },
  methods: {
    play () {
      // 如果使用者設定了auto值,則表示開啟自動播放功能
      // 每隔auto值,去調用一次:播放下一張
      if (this.auto) {
        this.timer = setInterval(() => {
          this.hNext()
        }, this.auto)
      }
    },
    stop () {
      if (this.timer) {
        clearInterval(this.timer)
      }
    },
    // 下一張圖
    hNext () {
      // 1. 把下标向後移
      // 2. 判斷是否越界
      this.currentIndex++
      if (this.currentIndex === this.list.length) {
        this.currentIndex = 0
      }
    },
    hPrev () {
      // 上一張圖
      // 1. 把下标向前移
      // 2. 判斷是否越界
      this.currentIndex--
      if (this.currentIndex === -1) {
        this.currentIndex = this.list.length - 1
      }
      // this.currentIndex = this.currentIndex === -1 ? this.list.length - 1 : this.currentIndex
    },
    // 滑鼠進入,如果自動播放的定時器,則要删除定時器,停止自動播放
    hMouseEnter () {
      this.stop()
    },
    // 滑鼠離開,如果有自動播放,則要繼續開啟定時器,自動播放
    hMouseLeave () {
      this.play()
    }
  },
  beforeDestroy () {
    this.stop()
  }
           

訓示條上滑鼠滑動也能切換

思路:

  • 給每個訓示條上的 li添加mouseenter事件,調整目前圖檔索引值

元件的子產品

仿Element-UI元件封裝系列6-輪播圖元件
<!-- 訓示條 -->
    <ol class="indirector">
      <!-- 隻有是目前要顯示的圖,才會添加current類 -->
      <li v-for="(item,idx) in list"
      :key="idx"
      :class="{current:idx===currentIdx}"
+      @mouseenter="currentIdx=idx"
      ></li>
    </ol>
           

emit事件

目标:

  • 讓使用者能夠監聽
    • 點選事件
    • 切換事件

在元件内部的某個場合下,抛出來事件。

  • 點選圖檔時
    <template>
      <div class="slider"
      @click="$emit('click',currentIndex)"
      @mouseleave="hMouseLeave"
      @mouseenter="hMouseEnter">
               
  • 切換時

    補充一個watch

watch: {
    currentIndex () {
      // console.log('目前的下标變了', this.currentIndex)
      this.$emit('slider', this.currentIndex)
    }
  }
           

測試代碼

<div class="about">
    <h1>對輪播元件測試</h1>
    <!--
      這裡的style會在元件的根元素上生效
    - list:是圖檔清單資料,它是一個數組,儲存要輪播圖檔資訊
    - auto:2000. 開啟自動播放。2000毫秒切換一張.
    - curIdx:初始播放第幾張
-->
    <my-slider
      style="width:550px;height:350px;"
      :list="list"
      :curIdx='2'
      auto='2000'
      @click="hClick"
      @slider="hSlider"
      >
    </my-slider>
  </div>
    
methods: {
    hClick (index) {
      alert(index)
      console.log('目前是', index, '被點選')
    },
    hSlider (idx) {
    	this.curIdx = idx
      console.log(idx)
    }
  }
           

切換動畫

清單動畫transition-group元件:

格式:

<transition-group>
 清單項1,清單項2......
</transition-group>
           

屬性值:

  • name: 與

    <transition>

    元件中的name意義一樣的,用于指定産生動畫的那個特殊的類名。
  • tag:要渲染成的dom類型.
    仿Element-UI元件封裝系列6-輪播圖元件

補充css

.fade-enter, .fade-leave-to {
  opacity: 0.8;
}
.fade-enter-active, .fade-leave-active {
  transition: all 0.5s;
}
           
仿Element-UI元件封裝系列6-輪播圖元件

補充說明圖檔的位址問題

如果傳入的圖檔是相對位址,則需要使用require()處理。

list: [
        {
          // 圖檔在本地,這裡采用相對位址。
          // 結果不對。http://localhost:8080/1.jpeg。這樣找不到。
          // url: './1.jpeg',
          url: require('./1.jpeg'),// http://localhost:8080/img/1.6034a006.jpeg
          alt: '圖檔在本地,這裡采用相對位址'
        },
        {
          url: 'http://contentcms-bj.cdn.bcebos.com/cmspic/ce6532660da8c9c92e10ae0e1ddfc866.jpeg?x-bce-process=image/crop,x_0,y_0,w_750,h_408',
          alt: 'babababa'
        }
]
           

e-leave-active {

transition: all 0.5s;

}

## 補充說明圖檔的位址問題

如果傳入的圖檔是相對位址,則需要使用require()處理。

           

list: [

{

// 圖檔在本地,這裡采用相對位址。

// 結果不對。http://localhost:8080/1.jpeg。這樣找不到。

// url: ‘./1.jpeg’,

url: require(’./1.jpeg’),// http://localhost:8080/img/1.6034a006.jpeg

alt: ‘圖檔在本地,這裡采用相對位址’

},

{

url: ‘http://contentcms-bj.cdn.bcebos.com/cmspic/ce6532660da8c9c92e10ae0e1ddfc866.jpeg?x-bce-process=image/crop,x_0,y_0,w_750,h_408’,

alt: ‘babababa’

}

]