1. 程式人生 > >詳解vue之better-scroll實現輪播圖和頁面滾動

詳解vue之better-scroll實現輪播圖和頁面滾動

(該方法只針對移動端使用效果較好,PC端不推薦,使用的版本是[email protected],其他版本會出錯)

1.安裝better-scroll

在根目錄中package.json的dependencies中新增:

"better-scroll": "^0.1.15"

然後 npm i 安裝。

2.封裝程式碼

將better-scroll封裝成兩個基礎元件slider和scroll放於src/base資料夾中。
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"></span> </div> </div> </template> <script
>
import {addClass} from '../common/js/dom' import BScroll from 'better-scroll' export default{ data() { return { dots:[], currentPageIndex: 0 } }, props:{ loop:{ type:Boolean, default:true }, autoPlay:{ type:Boolean
, default:true }, interval:{ type: Number, default:4000 } }, mounted() { this._setSliderWidth() setTimeout(() => { // 在初始化slider前初始化dot this._initDots() this._initSlider() if (this.autoPlay) { this._play() } }, 20) // 監聽視窗大小改變時間 window.addEventListener('resize', () => { if (!this.slider) { return } this._setSliderWidth(true) this.slider.refresh() }) }, methods:{ _setSliderWidth(isResize) { this.children = this.$refs.sliderGroup.children let width = 0 // slider 可見寬度 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 }) // 監聽滾動結束時間獲取pageX this.slider.on('scrollEnd', () => { let pageIndex = this.slider.getCurrentPage().pageX if (this.loop) { // 由於bscroll迴圈播放首尾各加一個,因此索引-1 pageIndex -= 1 } this.currentPageIndex = pageIndex if (this.autoPlay) { this._play() } }) this.slider.on('beforeScrollStart', () => { if (this.autoPlay) { clearTimeout(this.timer) } }) }, _initDots() { // 長度為n的空陣列 this.dots = new Array(this.children.length) }, _play() { // currentPageIndex為不含首尾副本的索引,因此若有迴圈要+2 let pageIndex = this.currentPageIndex + 1 if (this.loop) { pageIndex += 1 } this.timer = setTimeout(() => { this.slider.goToPage(pageIndex, 0, 400) }, this.interval) } }, // 生命週期destroyed銷燬清除定時器,有利於記憶體釋放 destroyed() { clearTimeout(this.timer) }, }
</script> <style scoped> .slider{ min-height: 1px; position: relative; } .slider-group{ position: relative; overflow: hidden; white-space: nowrap; } .slider-item{ float: left; box-sizing: border-box; overflow: hidden; text-align: center; height: 150px; overflow: hidden; } .slider-item a{ display: block; width: 100%; overflow: hidden; text-decoration: none; } .slider-item 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: red; } .active{ width: 20px; border-radius: 5px; } </style>

該程式碼引用common/js/dom.js中的addClass()方法為每個輪播圖新增一個slider-item類,dom.js程式碼如下:

export function hasClass (el, className) {
  // 開始或空白字元+類名+空白字元或結束
  let reg = new RegExp('(^|\\s)' + className + '(\\s|$)')
  // 測試元素是否有該類名,返回布林值
  return reg.test(el.className)
}

export function addClass (el, className) {
  if (hasClass(el, className)) {
    return
  }
// 以空白符為切割位置切割生成新陣列
  let newClass = el.className.split(' ')
// 陣列中加入新類名
  newClass.push(className)
// 將陣列元素放入一個字串,以空白符間隔
  el.className = newClass.join(' ')
}

scroll.vue程式碼

<template>
  <div ref="wrapper">
    <slot></slot>
  </div>
</template>

<script>
  import BScroll from 'better-scroll'

  export default {
    props: {
      probeType: {
        type: Number,
        default: 1
      },
      click: {
        type: Boolean,
        default: true
      },
      listenScroll: {
        type: Boolean,
        default: false
      },
      object: {
        type: Object,
        default: null
      },
      data: {
        type: Array,
        default: null
      },
      string: {
        type: String,
        default: ''
      },
      pullup: {
        type: Boolean,
        default: false
      },
      beforeScroll: {
        type: Boolean,
        default: false
      },
      refreshDelay: {
        type: Number,
        default: 20
      }
    },
    mounted() {
      setTimeout(() => {
        this._initScroll()
      }, 20)
    },
    methods: {
      _initScroll() {
        if (!this.$refs.wrapper) {
          return
        }
        this.scroll = new BScroll(this.$refs.wrapper, {
          probeType: this.probeType,
          click: this.click
        })

        if (this.listenScroll) {
          let me = this
          // pos為位置引數
          this.scroll.on('scroll', (pos) => {
            me.$emit('scroll', pos)
          })
        }

        if (this.pullup) {
          this.scroll.on('scrollEnd', () => {
            if (this.scroll.y <= (this.scroll.maxScrollY + 50)) {
              this.$emit('scrollToEnd')
            }
          })
        }

        if (this.beforeScroll) {
          this.scroll.on('beforeScrollStart', () => {
            this.$emit('beforeScroll')
          })
        }
      },
      disable() {
        this.scroll && this.scroll.disable()
      },
      enable() {
        this.scroll && this.scroll.enable()
      },
      refresh() {
        this.scroll && this.scroll.refresh()
      },
      scrollTo() {
        this.scroll && this.scroll.scrollTo.apply(this.scroll, arguments)
      },
      scrollToElement() {
        this.scroll && this.scroll.scrollToElement.apply(this.scroll, arguments)
      }
    },
    watch: {
      data() {
        setTimeout(() => {
          this.refresh()
        }, this.refreshDelay)
      },
      string() {
        setTimeout(() => {
          this.refresh()
        }, this.refreshDelay)
      },
      object() {
        setTimeout(() => {
          this.refresh()
        }, this.refreshDelay)
      }
    }
  }
</script>
<style>
</style>

3.使用封裝元件

使用這兩個元件的頁面元件home.vue 程式碼如下:

<template>
    <div>
    <scroll :data="su" class="scroll">
        <div>
            <div class="slider-wrapper">
                <slider>
                    <div v-for='item in slider'>
                        <a href="">
                            <img :src="item.url" alt="">
                        </a>
                    </div>
                </slider>
            </div>
            <ul v-for='item in su'>
                <li>{{item}}</li>
            </ul>
        </div>
    </scroll>
    </div>
</template>
<script>
    import Slider from '../base/slider'
    import Scroll from '../base/scroll'
export default {
    data () {
        return {
            slider: [
                {url: 'http://upload-images.jianshu.io/upload_images/7932253-54c81df0beed405b.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1080/q/50'},
                {url: 'https://y.gtimg.cn/music/photo_new/T003R720x288M000004ERTpn1UBu2f.jpg?max_age=2592000&max_age=2592000'},
                {url: 'https://y.gtimg.cn/music/photo_new/T003R720x288M00000077s7P0HaZpc.jpg?max_age=2592000&max_age=2592000'},
                {url: 'https://y.gtimg.cn/music/photo_new/T003R720x288M000001QL1Si05yMPq.jpg?max_age=2592000&max_age=2592000'},
                {url: 'https://y.gtimg.cn/music/photo_new/T003R720x288M000002ke7OC3ooZ5g.jpg?max_age=2592000&max_age=2592000'},
            ],
            su:[1,2,3,4,5,6,7,8,9,10,1,2,3,4,2,3,5,8,7,4,]
        }
    },
    methods: {
    },
    components: {
        Slider,
        Scroll
    }
}
</script>
<style>
.slider-wrapper{
    width: 100%;
    position: relative;
    overflow: hidden;
}
.scroll{
    height: 500px;
}
</style>

注意點:

  • slider元件的父元素必須給他一個100%的寬度且定義overflow:hidden,否則整個頁面會被撐開,整個頁面都能橫向滾動
  • scroll元件在引用時必須給他一個固定高度。只有擁有固定高度才會發生滾動。

效果圖如下: