目标:
- 實作一個輪播圖元件
仿Element-UI元件封裝系列6-輪播圖元件
基本步驟:
- 開發元件。packages/slider/slider.vue
- 添加到packages/index.js,全局注冊
- 在測試頁面中使用元件
準備工作
-
添加元件
建立一個元件
仿Element-UI元件封裝系列6-輪播圖元件 <template> <div class=''> 我是一個輪播圖元件 </div> </template> <script> export default { name: 'MySlider' } </script>
-
導出元件
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) } }
-
在測試項目中使用元件
(1)添加測試頁面
内容
<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>
效果
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
}
}
添加自動播放功能
目标:
- 允許使用者手動啟動輪播功能
思路:
- 在元件上添加
這個prop,它用來接收使用者的輸入,如果有值,則開啟定時器auto
- 在建立元件時,如果有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事件,調整目前圖檔索引值
元件的子產品
<!-- 訓示條 -->
<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: 與
元件中的name意義一樣的,用于指定産生動畫的那個特殊的類名。<transition>
- tag:要渲染成的dom類型.
仿Element-UI元件封裝系列6-輪播圖元件
補充css
.fade-enter, .fade-leave-to {
opacity: 0.8;
}
.fade-enter-active, .fade-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'
}
]
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’
}
]