在recommend.vue文件中,结合Better-scroll组件,开发轮播图组件,总结如下:
1.创建slider.vue并设置属性:是否循环、是否自动播放、播放间隔(通过props)
2.组件初始化(mounted钩子中)
3.外部容器根据轮播图片计算宽度
4.图片自动播放
5.底部dots高亮
6.处理一些细节:keep-alive、resize等
1.创建slider.vue组件
<template>
<div class="slider" ref="slider">
<div class="slider-group" ref="sliderGroup">
//外部调用组件的
<slot></slot>
</div>
<div class="dots">
<span class="dot" :class="{active:currentPageIndex === index}" v-for="(item,index) in dots" :key=index></span>
</div>
</div>
</template>
props: {
loop: {//可循环
type: Boolean,
default: true
},
autoPlay: { //自动播放
type: Boolean,
default: true
},
interval: {//间隔
type: Number,
default: 4000
}
}
2.组件初始化
mounted() {
//要等到dom加载完毕可以使用this.$nextTick,这里推荐使用 setTimeout(() => {}, 20)
//因为浏览器刷新dom是17ms,所以这里使用了20ms
setTimeout(() => {
this._setSliderWidth() //2.1初始化外部容器的宽度
this._initDots() //2.2初始化底部dots: initDost只能在此执行,这个时候this.children.length:5
//如果在initSlider后执行,因为已经new BScroll,并且this.loop,所以首尾添加两个元素this.children: 7
this._initSlider() //2.3初始化Better-scrolll轮播图
//根据属性设置,如果自动播放则自动播放
if (this.autoPlay) {
this._play()
}
}, 20)
·····
},
2.1初始化外部容器的宽度
//isResize:是否resize出发的重新计算
_setSliderWidth(isResize) {
this.children = this.$refs.sliderGroup.children//获取sliderGroup轮播图中几张图片
let width = 0
let sliderWidth = this.$refs.slider.clientWidth//取到当前的轮播图的宽度(即为父元素的宽度)
for (let i = 0; i < this.children.length; i++) {
let child = this.children[i]
//给每个轮播图加class, 轮播图的结构下a img 设置样式
addClass(child, 'slider-item')
child.style.width = sliderWidth + 'px' //设置每个轮播图的宽度
width += sliderWidth
}
//this.loop:如果是循环播放并且是首次初始化时,需要再加上头尾两个重复图片的宽度。
//isResize:当浏览器调整大小的时候,父元素中的子元素已经包含了首位重复图片的元素,
//因此不需要进行宽度的增加。
if (this.loop && !isResize) {
width += 2 * sliderWidth
}
//BScrol的横向是无法撑开的,因此需要根据内部几张图片的宽度来设置外部容器的宽度,
//即this.$refs.sliderGroup.style.width=轮播图图片宽度的和
this.$refs.sliderGroup.style.width = width + 'px'
},
2.2初始化底部dots
初始化dots的个数
_initDots() {
this.dots = new Array(this.children.length)
},
2.3初始化Better-scroll轮播图:最新版本的Better-scroll
_initSlider() {
//创建BScroll实例,并设置配置项,
this.slider = new BScroll(this.$refs.slider, {
scrollX: true, //横向移动
scrollY: false, //不能纵向移动
momentum: false,
snap: {
loop: this.loop,
threshold: 0.3,
speed: 400
}
})
//监听每一个图片滚动结束事件
this.slider.on('scrollEnd', this._onScrollEnd)
//touchend事件,手动拖动
this.slider.on('touchend', () => {
if (this.autoPlay) {
this._play()
}
})
//触发时机:滚动开始之前
this.slider.on('beforeScrollStart', () => {
if (this.autoPlay) {
clearTimeout(this.timer)
}
})
},
_onScrollEnd() {
//获取当前页面的信息,pageX 横轴方向页面数
let pageIndex = this.slider.getCurrentPage().pageX
//currentPageIndex给dots高亮使用
this.currentPageIndex = pageIndex
//如果循环播放,则播放下一页
if (this.autoPlay) {
this._play()
}
},
//轮播图播放
_play() {
//播放之前清除上一个,或者因为手动拖动导致定时器没有完全出发的‘剩余定时器’
clearTimeout(this.timer)
this.timer = setTimeout(() => {
//滚动到下一个页面
this.slider.next()
}, this.interval)
}
3.keep-alive
组件加上了keep-alive,所以在相应的生命周期中做相应处理
activated() {
this.slider.enable() //启用 better-scroll, 默认 开启
let pageIndex = this.slider.getCurrentPage().pageX //获取当前页面的信息,pageX 横轴方向页面数
//当我们做 slide 组件的时候,slide 通常会分成多个页面。调用此方法可以滚动到指定的页面
//参数:x 横轴的页数 y 纵轴的页数 time 动画执行的时间
this.slider.goToPage(pageIndex, 0, 0)
this.currentPageIndex = pageIndex
if (this.autoPlay) {
this._play()
}
},
deactivated() {
this.slider.disable() //禁用 better-scroll,DOM 事件(如 touchstart、touchmove、touchend)的回调函数不再响应
clearTimeout(this.timer) //清除定时器
},
4.组件beforeDestroy
页面切走,组件会调用destroyed来销毁实例,这时清除定时器,利于内存的释放
beforeDestroy() {
this.slider.disable()
clearTimeout(this.timer)
},
5.window.resize事件
在mouted生命周期钩子中定义此事件
window.addEventListener('resize', () => {
//enabled判断当前 scroll 是否处于启用状态
if (!this.slider || !this.slider.enabled) {
return
}
clearTimeout(this.resizeTimer)
this.resizeTimer = setTimeout(() => {
//判断当前 scroll 是否处于滚动动画过程中
//当开启 CSS3 Transition 动画时判断该值
if (this.slider.isInTransition) {
this._onScrollEnd()
} else {
if (this.autoPlay) {
this._play()
}
}
//重新计算 better-scroll,当 DOM 结构发生变化的时候务必要调用确保滚动的效果正常
this.refresh()
}, 60)
})
refresh() {
if (this.slider) {
this._setSliderWidth(true)
//重新计算 better-scroll,当 DOM 结构发生变化的时候务必要调用确保滚动的效果正常
this.slider.refresh()
}
},
6.使用
在recomment.vue中使用slider.vue轮播图
<div v-if="recommends.length" class="slider-wrapper">
<div class="slider-content">
<slider ref="slider">
<div v-for="item in recommends">
<a :href="item.linkUrl">
<img @load="loadImage" :src="item.picUrl">
</a>
</div>
</slider>
</div>
</div>
6.1 v-if="recommends.length"
在recommend.vue中,this.recommends数据的获取是异步过程,因此加上v-if指令,在数据获取后才渲染slider.vue组件
6.2 图片的加载过程
图片的加载onload是一个异步过程,因此图片加载完成后slider需要refresh()
而且只需要有一个图片加载完成即可,所以加个标志位checkloaded
loadImage() {
if (!this.checkloaded) {
this.checkloaded = true
setTimeout(() => {
this.$refs.scroll.refresh()
}, 20)
}
},
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。