基于 better-scroll 的轮播图组件
在做移动端轮播图的时候,本想沿用之前 PC 端的轮播图,后来发现并没有支持 touch 事件,所以采用了 better-scroll 来实现轮播图组件。
关于 better-scroll 的介绍可以看官方文档 传送门
正如 文档中所说,学习 bette-scroll 的相关组件 最好的方式就是去看 example 目录下的 demo,我现在要实现轮播图组件,直接找到轮播图的 demo 代码部分 源码传送门
首先实现轮播图的基本思路是通过 width 设置 overflow: hidden 来达到目的,通过 translate 移动图片实现轮播,这里 translate 的事情 better-scroll 已经替我们干了
首先看下轮播图的 html 结构:
<div class="slide" ref="slide">
<div class="slide-group" ref="slideGroup">
<slot></slot> // 通过 slot 父组件传入图片等自定义内容
</div>
<div v-show="showDot" class="dots">
<span class="dot" // 图片对应的下标
:class="{active: currentPageIndex === index}"
v-for="(item, index) in dots"
:key="index"
></span>
</div>
</div>
css 结构采用的是 stylus:
.slide
min-height: 1px
.slide-group
position: relative
overflow: hidden
white-space: nowrap
.slide-item
float: left
box-sizing: border-box
overflow: hidden
text-align: center
a
display: block
width: 100%
overflow: hidden
text-decoration: none
img
display: block
width: 100%
.dots
position: absolute
right: 0
left: 0
bottom: 12px
transform: translateZ(1px)
text-align: center
font-size: 0
.dot
display: inline-block
margin: 0 4px
width: 8px
height: 8px
border-radius: 50%
background: $color-text-l
&.active
width: 20px
border-radius: 5px
background: $color-text-ll
好了,下面我们看看如何组织下代码,首先我们需要看一下 better-scroll 的文档,就会知道 better-scroll 执行的时机是 dom 结构渲染完毕后,所以最好的执行时机可以通过 this.$nextTick() 去实现,当然也可以通过定时器 setTimeout(fn, 20) 这样一个经验值去实现也是没有问题的。
确定好了执行时机,我们开始初始化相关内容,比如轮播容器宽度以及 better-scroll 等
初始化 slide 宽度
// 获取图片的 clientWidth
// 当需要循环播放的时候在首尾添加两个图片的 clientWidth,做一个过渡
// 给整个 slideGroup 设置 width
_setSliderWidth(isResize) {
this.children = this.$refs.slideGroup.children
let width = 0
let slideWidth = this.$refs.slide.clientWidth
for (let i = 0; i < this.children.length; i++) {
let child = this.children[i]
addClass(child, 'slide-item')
child.style.width = slideWidth + 'px'
width += slideWidth
}
if (this.loop && !isResize) {
width += 2 * slideWidth
}
this.$refs.slideGroup.style.width = width + 'px'
}
初始化 slide 宽度后可以初始化 better-scroll 了
// 这个配置直接直接看文档就好了,这里我们监听了 scrollEnd 事件获取当前页数
this.slider = new BScroll(this.$refs.slide, {
scrollX: true,
scrollY: false,
momentum: false,
snap: {
loop: this.loop,
threshold: this.threshold,
speed: this.speed
}
})
this.slider.on('scrollEnd', () = >{
this.currentPageIndex = this.slider.getCurrentPage().pageX
if (this.autoPlay) this._play()
})
初始化 dot
// 初始化对应轮播数量的 dot 即可
this.dots = new Array(this.children.length)
自动播放
// 通过定时器调用 next 接口即可
clearTimeout(this.timer)
this.timer = setTimeout(() => {
this.slider.next()
}, this.interval)
mounted 阶段初始化,并且监听 resize 事件
setTimeout(() = >{
this._setSliderWidth()
if (this.showDot) this._initDots()
this._initSlider()
if (this.autoPlay) this._play()
}, 20)
window.addEventListener('resize', () = >{
if (!this.slider) return
this._setSliderWidth(true)
this.slider.refresh()
})
还有一个注意的点,当组件中用到了计时器时,要在 destroyed 阶段清除定时器,释放内存
destoryed() {
clearTimeout(this.timer)
}
然后我们再父组件引用 slider 组件即可
<div v-if="recommends.length" class="slider-wrapper">
<div class="slider-content">
<slider>
<div v-for="(item, index) in recommends" :key="index">
<a :href="item.linkUrl">
<img :src="item.picUrl">
</a>
</div>
</slider>
</div>
</div>
不得不说 better-scroll 真的很强大,最终的展示效果如下: