轮播图在项目中,是属于必不可少的一环,基本每一个项目都会有,网上的轮播图插件也有千千万万,不能否认它们的功能都很是的强大,但有的时候,咱们只是须要简简单单的轮播,而不须要那么多的动画功能和、特效,而我又有强烈的代码洁癖,虽说不要反复造轮子,可是本身穿多大码的鞋子,本身知道,根据本身的业务,本身动手作一个,即轻小,又能够锻炼本身的动手能力,一举夺得呢。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
至此,一个轮播图组件就完成啦,是否是很简单呢?本人也是第一次写,若是您有任何的问题或者建议,咱们一块儿讨论哦。函数
关于我:非科班出身的不知名应届毕业生、代码洁癖患者,梦想有朝一日成为前端大牛。
复制代码