輪播圖在項目中,是屬於必不可少的一環,基本每一個項目都會有,網上的輪播圖插件也有千千萬萬,不能否認它們的功能都很是的強大,但有的時候,咱們只是須要簡簡單單的輪播,而不須要那麼多的動畫功能和、特效,而我又有強烈的代碼潔癖,雖說不要反覆造輪子,可是本身穿多大碼的鞋子,本身知道,根據本身的業務,本身動手作一個,即輕小,又能夠鍛鍊本身的動手能力,一舉奪得呢。css
首先來看看我實現的效果,全屏寬: html
非全屏寬,局部效果: 看起來仍是不錯的,如今讓咱們來開始吧!<template>
<div class="swiper" ref="swiper">
<div class="swiper-list" :style="{width: swiperListWidth + 'px', transform: 'translateX(' + translateX + 'px)', transitionDuration: transitionDuration + 's' }" ref="swiperList">
<slot></slot>
</div>
<div class="dot">
<span v-for="(x, i) in sum" :key="'dot' + x" :class="{'on': i === index}"></span>
</div>
</div>
</template>
複製代碼
這裏我使用了<slot>
插槽,這樣能夠由父組件直接操做,而不須要傳入反覆組件傳參前端
<style lang="less">
.swiper {
position: relative;
overflow: hidden;
.swiper-list {
display: flex;
transition-property: all;
transition-timing-function: cubic-bezier(0.18, 0.89, 0.32, 1.28);
}
.swiper-item {
width: 100%;
}
img {
display: block;
}
.dot {
display: flex;
position: absolute;
width: 100%;
margin-top: -15px;
justify-content: center;
span {
@size: 8px;
width: @size;
height: @size;
background-color: rgba(255, 255, 255, .5);
border-radius: 50%;
&.on {
background-color: #fff;
}
& + span {
margin-left: 5px;
}
}
}
}
</style>
複製代碼
我習慣使用less
進行css編寫,在本組件中,我使用了flex
佈局,而沒有使用float
佈局,由於張鑫旭大大曾說過,浮動是魔鬼啊。 我在此並無打開vue的scoped
屬性,雖然這樣可能會形成和其餘組件衝突,因爲個人業務比較簡單,並且打開scoped
就不能由本組件控制父組件的樣式,因此就不打開scoped
屬性了,固然爲了防止衝突,還能夠設立本身的命名空間,好比把輪播圖的類名設爲xiaoming-swiper
,以本身的名字命名,總不太可能引發css衝突了吧?vue
<script>
export default {
name: 'swiper',
data () {
return {
swiperWidth: '', // 輪播圖盒子的寬度
index: 0, // 輪播圖序號
transitionDuration: 0.5, // 切換動畫時長
timer: '', // 定時器
startX: '', // touchstart的起始x座標
offset: '' // move偏移值
}
},
props: {
// 我在這裏設置了必填的一個屬性,爲了避免去計算輪播圖的總數量
sum: {
type: Number,
required: true
},
time: {
type: Number,
default: 3000
}
},
computed: {
// 輪播圖列表的寬度
swiperListWidth () {
return this.swiperWidth * this.sum
},
// 輪播圖列表偏移值
translateX () {
return this.index * this.swiperWidth * -1
}
},
created () {
this.$nextTick(() => {
let swiper = this.$refs.swiper
// 爲何不取屏幕寬度,是由於通用性,由外部的盒子決定輪播圖的寬
this.swiperWidth = swiper.offsetWidth
this.autoPlay()
// addEventListener不能夠用匿名函數,由於沒法解除綁定
swiper.addEventListener('touchstart', this.touchStart)
swiper.addEventListener('touchmove', this.touchMove)
swiper.addEventListener('touchend', this.touchEnd)
})
},
methods: {
autoPlay () {
this.timer = setInterval(() => {
let index = this.index + 1
// 取餘數運算,0%5=0,1%5=1,5%5=0,固然用if判斷語句也是能夠的
this.index = index % this.sum
}, this.time)
},
touchStart (e) {
this.transitionDuration = 0
clearInterval(this.timer)
// 只記錄第一根手指觸發的值
this.startX = e.targetTouches[0].clientX
},
touchMove (e) {
this.offset = this.startX - e.targetTouches[0].clientX
this.$refs.swiperList.style.transform = `translateX(${this.translateX - this.offset}px)`
},
touchEnd (e) {
this.transitionDuration = 0.5
// 計算偏移值四捨五入,若是拖動距離大於等於0.5則換一張輪播圖
let num = Math.round(this.offset / this.swiperWidth)
let sum = this.index + num
// 先計算再賦值給this.index避免重複觸發計算屬性,爲何這裏不取餘數,是由於又負數出現
if (sum > this.sum - 1) {
sum = 0
} else if (sum < 0) {
sum = this.sum - 1
}
// 解決拖動距離小於一半,index值無變化,沒法觸發計算屬性,主動還原偏移值
if (sum === this.index) {
this.$refs.swiperList.style.transform = `translateX(${this.translateX}px)`
} else {
this.index = sum
}
this.autoPlay()
}
},
// 實例銷燬以前,移除綁定事件
beforeDestroy () {
let swiper = this.$refs.swiper
swiper.removeEventListener('touchstart', this.touchStart)
swiper.removeEventListener('touchmove', this.touchMove)
swiper.removeEventListener('touchend', this.touchEnd)
}
}
</script>
複製代碼
import swiper from './assembly/swiper.vue'
Vue.component('swiper', swiper)
複製代碼
我在本項目中使用的是全局註冊組件,由於這個輪播圖須要使用的地方有點多。若是你的項目中輪播圖使用的不多,那我推薦局部註冊。bash
<div class="box">
<swiper :sum="5">
<router-link to="/goods/1" v-for="x of 5" :key="x" class="swiper-item">
<img src="https://user-gold-cdn.xitu.io/2017/12/27/1609723e9449c30e">
</router-link>
</swiper>
</div>
複製代碼
你可能發現了,我在組件外嵌套了一層box
,這是由於我用的是這個box
來控制輪播圖的寬度。less
至此,一個輪播圖組件就完成啦,是否是很簡單呢?本人也是第一次寫,若是您有任何的問題或者建議,咱們一塊兒討論哦。函數
關於我:非科班出身的不知名應屆畢業生、代碼潔癖患者,夢想有朝一日成爲前端大牛。
複製代碼