vue-music (3) 轮播图
程序员文章站
2022-03-07 17:19:12
...
抽象出一个轮播图组件slider
<div class="slider" ref="slider">
<div class="slider-group" ref="sliderGroup">
<slot>
</slot>
</div>
<div class="dots">
<span class="dot" v-for="(item,index) in dots" :key="index" :class="{'active':currentPageIndex === index}"></span>
</div>
</div>
slot插槽,将在父组件中包裹着数据带到子组件,然后将完整的子组件返回
props: { // **接受外部组件对该轮播图组件的控制**
loop: {
type: Boolean,
default: true // 设置循环状态
},
autoPlay: { // 自动播放
type: Boolean,
default: true
},
interval: { // 轮播时间间隔
type: Number,
default: 3000
}
},
mounted (){ // mounted钩子函数,初始化better-scroll
setTimeout(() => { // *页面刷新的时间在17ms左右,所以设置延时20ms,等待dom加载完成*
this.__setSliderWidth() // 初始化 设置容器宽度
this._initDots() // 初始化 轮播图下面的小点点
thsi._initSlider() // 初始化bscroll
if (this.autoPlay) {
this._play()
}
}, 20)
// 当改变屏幕大小(横屏)时需要重新计算sliderwidth
window.addEventListener('resize', () => {
if (!this.sliderScroll) {
return // 已经初始化了 就不需要再计算
}
this._setSliderWidth(true) // 重新初始化容器宽度 *true为标志*
this.sliderScroll.refresh() // 调用scroll组件中的refresh()方法刷新
})
},
destroyed () { // 当页面切走的时候,组件会调用destroyed来销毁实例。所以在这个时候要清除定时器,这是一个好的变成习惯,利于内存的释放
clearTimeout(this.timer)
},
created()和mounted()差别
created:在模板渲染成html前调用,即通常初始化某些属性值,然后再渲染成视图。使用的较多
mounted:在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作,使用较少,在子组件使用,父组件使用created,子组件用mounted。在vue生命周期中只执行一次. slider组件需要等图片数据渲染完才能计算出宽度,所以用mounted()
methods: {
_setSliderWidth(isResized){ // *判断是否需要重新计算宽度 即有没有横屏情况*
this.children = this.$refs.sliderGroup.children // 轮播图有多少个li 即有多少张图片
let winth = 0 // 定义初始width
let sliderWidth = this.$refs.slider.clientWidth // 定义每个图片的宽度 即屏幕可视区宽度
for(let i=0; i<this.children;i++){ // 遍历所有图片
let children = this.children[i]
addClass(child, 'slider-item') // *调用外部方法* 给每个li 添加新的slider-item的class
child.style.width = sliderWidth + 'px' // 每个图片 li都赋值width属性 为屏幕宽度 *stylu.width 要有单位*
width += sliderWidth // 有几个图片,循环几次,就加几次屏幕宽度 最终得到容器总宽度
}
if (this.loop && !isResized) { // 当isResized为true时候,就不用再头尾各加一张图片 为了使以下代码就再初始化的时候执行一次
width += 2 * sliderWidth // 因为循环播放,所以要在头尾各家一张图片
}
this.$refs.sliderGroup.style.width = width // 容器ul的宽度就是width
},
}
addClass()给el添加class属性的方法
// addClass()
function addclass(el, claName){
if (hasClass(el, claName){
return // 若有该class 直接返回
}
let ClassArr = el.className.split('') // 将el所有的class拆成数组
ClassArr .push(claName) // 将新class插入到el的class数组中
el.className = ClassArr .join('') // 将class数组变成字符串
}
// 添加之前先判断有没有该class
function hasClass(el, claName){
let reg = new RegExp('(^|\\s)' + claName + '(\\s|$)') // 正则判定
return reg.test(el.claName)
}
methods: {
_initSlider(){ // BScroll初始化 设置
this.sliderScroll = new BScroll(this.$refs.slider, {
scrollX: true, // 横向滑动
scrollY: false, // 竖向滑动
momentum: false,
snap: { // 轮播设置
loop: this.loop, // 循环
threshold: 0.3, // 动画时间
speed: 400 // 轮播间隔
}
})
this.sliderScroll.on('scrollEnd', () => { // BScroll 自带监听事件, 监听滚动结束后,执行回调函数
let pageIndex = this.sliderScroll.getCurrentPage().pageX // BScroll自带方法 获得当前图片***
this.currentPageIndex = pageIndex // 通过此值,动态给 小点点 active 属性
})
if (this.autoPlay) { // 防止手动滑动,它又自己滑动顺序错乱
clearTimeout(this.timer) // 每次轮播一张后去掉定时器
this._play()
}
},
_initDots(){
this.dots = new Array(this.children.lenfth) // 几个li就几个点点
}
_play(){ // 真正轮播的函数
this.setTimeout(() => {
this.sliderScroll.next() // BScroll内置方法, 跳到下一张
},this.interval) // 可以接受父组件传值
}
}
通过 :class 计算属性动态加active
<span class="dot" v-for="(item,index) in dots" :key="index" :class="{'active':currentPageIndex === index}"></span>