基于 better-scroll 的輪播圖組件
在做移動(dòng)端輪播圖的時(shí)候,本想沿用之前 PC 端的輪播圖炉峰,后來(lái)發(fā)現(xiàn)并沒有支持 touch 事件畏妖,所以采用了 better-scroll 來(lái)實(shí)現(xiàn)輪播圖組件。
關(guān)于 better-scroll 的介紹可以看官方文檔 傳送門
正如 文檔中所說(shuō)疼阔,學(xué)習(xí) bette-scroll 的相關(guān)組件 最好的方式就是去看 example 目錄下的 demo戒劫,我現(xiàn)在要實(shí)現(xiàn)輪播圖組件,直接找到輪播圖的 demo 代碼部分 源碼傳送門
首先實(shí)現(xiàn)輪播圖的基本思路是通過 width 設(shè)置 overflow: hidden 來(lái)達(dá)到目的婆廊,通過 translate 移動(dòng)圖片實(shí)現(xiàn)輪播迅细,這里 translate 的事情 better-scroll 已經(jīng)替我們干了
首先看下輪播圖的 html 結(jié)構(gòu):
<div class="slide" ref="slide">
<div class="slide-group" ref="slideGroup">
<slot></slot> // 通過 slot 父組件傳入圖片等自定義內(nèi)容
</div>
<div v-show="showDot" class="dots">
<span class="dot" // 圖片對(duì)應(yīng)的下標(biāo)
:class="{active: currentPageIndex === index}"
v-for="(item, index) in dots"
:key="index"
></span>
</div>
</div>
css 結(jié)構(gòu)采用的是 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 的文檔茵典,就會(huì)知道 better-scroll 執(zhí)行的時(shí)機(jī)是 dom 結(jié)構(gòu)渲染完畢后,所以最好的執(zhí)行時(shí)機(jī)可以通過 this.$nextTick() 去實(shí)現(xiàn)宾舅,當(dāng)然也可以通過定時(shí)器 setTimeout(fn, 20) 這樣一個(gè)經(jīng)驗(yàn)值去實(shí)現(xiàn)也是沒有問題的统阿。
確定好了執(zhí)行時(shí)機(jī),我們開始初始化相關(guān)內(nèi)容筹我,比如輪播容器寬度以及 better-scroll 等
初始化 slide 寬度
// 獲取圖片的 clientWidth
// 當(dāng)需要循環(huán)播放的時(shí)候在首尾添加兩個(gè)圖片的 clientWidth扶平,做一個(gè)過渡
// 給整個(gè) slideGroup 設(shè)置 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 了
// 這個(gè)配置直接直接看文檔就好了,這里我們監(jiān)聽了 scrollEnd 事件獲取當(dāng)前頁(yè)數(shù)
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
// 初始化對(duì)應(yīng)輪播數(shù)量的 dot 即可
this.dots = new Array(this.children.length)
自動(dòng)播放
// 通過定時(shí)器調(diào)用 next 接口即可
clearTimeout(this.timer)
this.timer = setTimeout(() => {
this.slider.next()
}, this.interval)
mounted 階段初始化蔬蕊,并且監(jiān)聽 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()
})
還有一個(gè)注意的點(diǎn)结澄,當(dāng)組件中用到了計(jì)時(shí)器時(shí),要在 destroyed 階段清除定時(shí)器岸夯,釋放內(nèi)存
destoryed() {
clearTimeout(this.timer)
}
然后我們?cè)俑附M件引用 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>
不得不說(shuō) better-scroll 真的很強(qiáng)大概而,最終的展示效果如下: