知识点:
- better-scroll的生命周期函数:scrollEnd(页面滚动完自动调用),beforeScrollStart(页面滚动前调用)
- 计算slider的宽度:slider的宽度 = 轮播图的数量 * 一个图片容器的宽度。如果你要实现循环播放,你就要多给两个图片容器的宽度,这个better-scroll内部用来克隆第一个和最后一个轮播图,实现无缝首尾切换。
- 屏幕大小变化的时候为什么不需要两倍的宽度? 答:因为已经克隆了第一个和最后一个轮播图,dom上已经多了两个轮播图元素,此时我们this.children.length是比一开始的this.children.length多2的,所有就不需要再循环外加多两倍的宽度。因为在for循环中宽度已经加了两倍的宽度(循环次数比第一次执行的时候多两遍)。
better-scroll版本更新:
- this.slider.getCurrentPage.pageX:以前版本的是从1开始,现在最新版是从0开始,跟我们dot的下标刚好对应,不用在加工。scrollEnd的代码如下:
// 滚动完的生命周期函数
this.slider.on('scrollEnd', () => {
// 滚动完就当前页的下标赋值给currentPageIndex
this.currentPageIndex = this.slider.getCurrentPage().pageX
// 自动播放就执行
if (this.autoPlay) {
this._play()
}
})
- this.slider.goToPage():由于版本的更替,使用goToPage这个方法来进行切换有点复杂(不知道是我不会,还是实现不了了),我使用next()来代替goToPage方法,因为我们配置了loop参数,我们一直调用next()方法,better-scroll会帮我们自动的循环切换到下一张轮播图。_play方法代码如下:
_play () {
this.timer = setTimeout(() => {
// 跳到下一页,因为我们设置配置参数loop为true,better-scroll会帮我们自动循环,我们直接跳下一页就行了
this.slider.next()
}, this.interval)
}
下面是整个slider组件的代码,我已经写好了注释,如果有什么不懂的,可以在评论区里留言或私信我,一起探讨,一起进步!
<template>
<div class="slider" ref="slider">
<div class="slider-group" ref="sliderGroup">
<slot></slot>
</div>
<div class="dot-group">
<span
class="dot"
v-for="(item,index) in dots"
:key="index"
:class="{'dot-active': index === currentPageIndex}"
@click="changeSliderPage(index)"
></span>
</div>
</div>
</template>
<script>
import BScroll from 'better-scroll'
import { addClass } from 'common/js/dom'
export default {
data () {
return {
dots: [], // 用于存储轮播图的数量
currentPageIndex: 0 // 正在显示图片的下标
}
},
props: {
// 是否循环
loop: {
type: Boolean,
default: true
},
// 自动播放
autoPlay: {
type: Boolean,
default: true
},
// 时间间隔
interval: {
type: Number,
default: 2000
}
},
mounted () {
// 在mounted钩子执行后的20ms,dom才完全渲染完
setTimeout(() => {
// 初始化slider容器的宽度
this._initSliderWidth()
// 初始化小点
this._initDot()
// 初始化轮播图
this._initSlider()
}, 20)
// 监听显示大小的变化
window.onresize = () => {
// 重新计算slider的宽度
this._initSliderWidth(true)
// better-scroll重新计算高度
this.slider.refresh()
}
},
// 使用了keep-alive组件包裹了,有activated钩子
activated () {
if (this.autoPlay) {
this._play()
}
},
// 离开该组件,就清除计时器
deactivated () {
clearTimeout(this.timer)
},
methods: {
_initSliderWidth (isResize) {
// 获取插槽中的子元素
this.children = this.$refs.sliderGroup.children
const sliderWidth = this.$refs.slider.clientWidth
// 用于存储slider的总宽度
let width = 0
for (let i = 0; i < this.children.length; i++) {
addClass(this.children[i], 'slider-item')
this.children[i].style.width = `${sliderWidth}px`
width += sliderWidth
}
// isResize是用于第二次渲染的,第二次渲染不需要宽度 * 2,因为页面上已经多了两个dom元素
if (this.loop && !isResize) {
width += 2 * sliderWidth
}
this.$refs.sliderGroup.style.width = `${width}px`
},
_initDot () {
this.dots = new Array(this.children.length)
},
_initSlider () {
this.slider = new BScroll(this.$refs.slider, {
scrollX: true, // x轴滚动
scrollY: false, // y轴滚动
momentum: false, // 取消滑多少,滚多少。让你滑动一点,就可以滚动下一页或上一页
snap: { // 轮播图的精髓
loop: this.loop, // 循环播放
threshold: 0.3 // 当你滑动30%页面的时候,就滚动上一页或者下一页
},
click: true // 允许点击事件
})
if (this.autoPlay) {
clearTimeout(this.timer)
this._play()
}
// 滚动完的生命周期函数
this.slider.on('scrollEnd', () => {
// 滚动完就当前页的下标赋值给currentPageIndex
this.currentPageIndex = this.slider.getCurrentPage().pageX
// 自动播放就执行
if (this.autoPlay) {
this._play()
}
})
// 开始滚动前的生命周期函数
this.slider.on('beforeScrollStart', () => {
if (this.autoPlay) {
clearTimeout(this.timer)
}
})
},
_play () {
this.timer = setTimeout(() => {
// 跳到下一页,因为我们设置配置参数loop为true,better-scroll会帮我们自动循环,我们直接跳下一页就行了
this.slider.next()
}, this.interval)
},
// 点击对应小点,跳到对应图片
changeSliderPage (index) {
this.slider.goToPage(index, 0, 400)
}
},
destroyed () {
// 清除定时器
clearTimeout(this.timer)
}
}
</script>
<style scoped lang="less">
.slider {
position: relative;
.slider-group {
position: relative;
overflow: hidden;
.slider-item {
float: left;
img {
display: block;
width: 100%;
}
}
}
.dot-group {
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
.dot {
display: inline-block;
width: 8px;
height: 8px;
background-color: #b0acb2;
border-radius: 50%;
margin-left: 5px;
}
.dot-active {
width: 15px;
border-radius: 5px;
background-color: #cccdd1;
}
}
}
</style>