做一个vue轮播图组件

vue

根据huangyi老师的慕课网vue项目跟着做的,下面大概记录了下思路

1.轮播图的图

先不做轮播图逻辑部分,先把数据导进来,看看什么效果。在recommend组件新建一个recommends的数组,用这个数组来接受数据的录播图部分。然后再轮播图的插槽部分添加图片,代码如下

<slider>

<div v-for="(item,index) in recommends" :key="index">

<a :href="item.linkUrl">

<img :src="item.picUrl">

</a>

</div>

</slider>

// recommends.vue

<script>

data() {

return {

recommends: []

}

},

methods: {

_getRecommend() {

getRecommend().then(res => {

if (res.code === ERR_OK) {

this.recommends = res.data.slider

console.log(this.recommends)

}

)

}

},

</script>

但是现在轮播图是糊的,所以就要按着需求来自己做slider组件。

首先,我们给轮播图sliderGroup,设置一个总的宽度。

<div class="slider" ref="slider">

<div class="slider-group" ref="sliderGroup">

<slot></slot>

</div>

<div class="dots"></div>

</div>

要设置sliderGroup的宽度的话,我们要在渲染好dom元素的时候再设置宽度,所以我们要在mouted这个钩子函数里执行设置宽度,_setSliderWidth()和 _initSlider()分别是设置宽度和加入滚动效果。这里是为了分离,不让mounted这个钩子函数里有太多东西,然后不好改逻辑。

mounted() {

setTimeout(() => {

this._setSliderWidth()

this._initSlider()

}, 20)

},

下面就是设置SliderGroup的宽度,其实中我们设置的主要方法,就是把slider的宽度给sliderGroupd的每个children,其中的slider-item属性是让他们左浮动的。然后让他们超出来的都隐藏掉。然后最后因为loop是循环轮播,要给slider前后各加一个宽度,这个是基础了,不懂得百度轮播图原理。然后最后让sliderGroup的宽度变成通过slot传进来的图片加2的宽度。

methods: {

_setSliderWidth() {

this.children = this.$refs.sliderGroup.children

let width = 0

let sliderWidth = this.$refs.slider.clientWidth

for (let i = 0; i < this.children.length; i++) {

let child = this.children[i]

addClass(child, 'slider-item')

child.style.width = sliderWidth + 'px'

width += sliderWidth

}

if (this.loop) {

width += 2 * sliderWidth

}

this.$refs.sliderGroup.style.width = width + 'px'

}

}

addClass方法不是系统自带的,是自己定义的,放在项目的src/common/js/dom/js里

export function addClass(el, className) {

if (hasClass(el, className)) {

return

}

let newClass = el.className.split(' ')

newClass.push(className)

el.className = newClass.join(' ')

}

export function hasClass(el, className) {

let reg = new RegExp('(^|\\s)' + className + '(\\s|$)')

return reg.test(el.className)

}

在设置完宽度以后,需要在recommend.vue设置一下加入addClass的时间,因为getRecommend这个方法是异步的,所以如果在dom渲染完后的时候在执行addclass方法,此时还没有获得到数据,所以也就没有slot里面的数据,所以要在slder组件外侧的div中设置一个v-if

<div v-if="recommends.length" class="slider-wrapper">

<slider>

<div v-for="(item,index) in recommends" :key="index">

<a :href="item.linkUrl">

<img :src="item.picUrl">

</a>

</div>

</slider>

</div>

当轮播图可以正确显示的时候,我们需要给轮播图添加滑动。我们用better-scroll,直接在npm上安装,然后在script标签里引入BScroll, 然后传入合适的参数,就可以了。

 _initSlider() {

this.slider = new BScroll(this.$refs.slider, {

scrollX: true,

scrollY: false,

momentum: false,

snap: true,

snapLoop: this.loop,

snapThreshold: 0.3,

snapSpeed: 400,

click: true

})

}

2.轮播图的dots

首先,我们要通过children.length来新建dots,在哪里新建呢?在mounted里

mounted() {

setTimeout(() => {

this._setSliderWidth()

this._initDots()

this._initSlider()

}, 20)

}

然后顺应着新建一个_initDots方法,这样可以有效的分离,业务逻辑比较清晰。

_initDots() {

this.dots = new Array(this.children.length)

},

现在的程度是仅仅有dots的静态了(css做出样式),然后我们需要根据页面来设置active-dots。所以我们需要在_initSlider()方法中监听scrollEnd事件,这个时间是better-scroll的,如果没导入就没有。

this.slider.on('scrollEnd', () => {

let pageIndex = this.slider.getCurrentPage().pageX

// 这个pageIndex -1是因为前面有一张为了无缝连接轮播图的。需要把他弄掉

if (this.loop) {

pageIndex -= 1

}

this.currentPageIndex = pageIndex

})

然后配合js,我们在html绑定相应的class就行了。

 <div class="dots">

<span

class="dot"

v-for="(item,index) in dots"

:key="index"

:class="{active:currentPageIndex === index}"

></span>

</div>

这样就就可以实现轮播带着dots一起动的效果了,接下来做自动播放功能

3. 轮播图自动播放

自动播放的时机,就是在新建轮播图完成的时候,也就是在mounted钩子里,定义一个_play方法

 mounted() {

setTimeout(() => {

this._setSliderWidth()

this._initDots()

this._initSlider()

if (this.autoPlay) {

this._play()

}

}, 20)

}

然后我们顺着去找methods里定义_play()这个方法。

_play() {

let pageIndex = this.currentPageIndex + 1

if (this.loop) {

pageIndex += 1

}

this.timer = setTimeout(() => {

// 0 代表y方向,400表示间隔

this.slider.goToPage(pageIndex, 0, 400)

}, this.interval)

}

但是这个在mounted钩子里,我们只调用了依次goToPage方法。这很不爽。所以需要我们在想办法,让每次换页的时候都去调用一下,拿着还不好说嘛,用上次的scrollEnd事件,所以只需要在上次那个地方添加一些方法就OK了

this.slider.on('scrollEnd', () => {

let pageIndex = this.slider.getCurrentPage().pageX

if (this.loop) {

pageIndex -= 1

}

this.currentPageIndex = pageIndex

if (this.autoPlay) {

clearTimeout(this.timer)

this._play()

}

})

OK,现在轮播图的dots,滑动,自动播放功能就完成了。下面是组件完整的代码

<template>

<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>

</template>

<script type="text/ecmascript-6">

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: 4000

}

},

mounted() {

setTimeout(() => {

this._setSliderWidth()

this._initDots()

this._initSlider()

if (this.autoPlay) {

this._play()

}

}, 20)

window.addEventListener('resize', () => {

if (!this.silder) {

return

}

this._setSliderWidth(true)

this.slider.refresh()

})

},

methods: {

_setSliderWidth(isResize) {

this.children = this.$refs.sliderGroup.children

let width = 0

let sliderWidth = this.$refs.slider.clientWidth

for (let i = 0; i < this.children.length; i++) {

let child = this.children[i]

addClass(child, 'slider-item')

child.style.width = sliderWidth + 'px'

width += sliderWidth

}

if (this.loop && !isResize) {

width += 2 * sliderWidth

}

this.$refs.sliderGroup.style.width = width + 'px'

},

_initSlider() {

this.slider = new BScroll(this.$refs.slider, {

scrollX: true,

scrollY: false,

momentum: false,

snap: true,

snapLoop: this.loop,

snapThreshold: 0.3,

snapSpeed: 400,

click: true

})

this.slider.on('scrollEnd', () => {

let pageIndex = this.slider.getCurrentPage().pageX

if (this.loop) {

pageIndex -= 1

}

this.currentPageIndex = pageIndex

if (this.autoPlay) {

clearTimeout(this.timer)

this._play()

}

})

},

_initDots() {

this.dots = new Array(this.children.length)

},

_play() {

let pageIndex = this.currentPageIndex + 1

if (this.loop) {

pageIndex += 1

}

this.timer = setTimeout(() => {

// 0 代表y方向,400表示间隔

this.slider.goToPage(pageIndex, 0, 400)

}, this.interval)

}

}

}

</script>

<style scoped lang="stylus" rel="stylesheet/stylus">

@import '~common/stylus/variable'

.slider

min-height: 1px

.slider-group

position: relative

overflow: hidden

white-space: nowrap

.slider-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

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

</style>

以上是 做一个vue轮播图组件 的全部内容, 来源链接: utcz.com/z/378034.html

回到顶部