根據huangyi老師的慕課網vue項目跟着作的,下面大概記錄了下思路javascript
先不作輪播圖邏輯部分,先把數據導進來,看看什麼效果。在recommend組件新建一個recommends的數組,用這個數組來接受數據的錄播圖部分。而後再輪播圖的插槽部分添加圖片,代碼以下css
<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組件。html
首先,咱們給輪播圖sliderGroup,設置一個總的寬度。vue
<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這個鉤子函數裏有太多東西,而後很差改邏輯。java
mounted() { setTimeout(() => { this._setSliderWidth() this._initSlider() }, 20) },
下面就是設置SliderGroup的寬度,其實中咱們設置的主要方法,就是把slider的寬度給sliderGroupd的每一個children,其中的slider-item屬性是讓他們左浮動的。而後讓他們超出來的都隱藏掉。而後最後由於loop是循環輪播,要給slider先後各加一個寬度,這個是基礎了,不懂得百度輪播圖原理。而後最後讓sliderGroup的寬度變成經過slot傳進來的圖片加2的寬度。npm
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-ifapp
<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, 而後傳入合適的參數,就能夠了。dom
_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 }) }
首先,咱們要經過children.length來新建dots,在哪裏新建呢?在mounted裏ecmascript
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一塊兒動的效果了,接下來作自動播放功能
自動播放的時機,就是在新建輪播圖完成的時候,也就是在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>