【四】轮播图组件

作者: 大海爱奔跑 | 来源:发表于2020-02-25 10:33 被阅读0次

    关于专题【vue开发音乐App】

    轮播图属于基础组件,所以归纳到src/base/slide里面,新建slide.vue

    一、html部分

    <template>
      <div class="slider" ref="slider">
        <div class="slider-group" ref="sliderGroup">
          <slot>
          </slot>
        </div>
        <div class="dots">
          <span class="dot" v-for="(item, i) in dots" :key="i" :class="{active : currIndex === i}"></span>
        </div>
      </div>
    </template>
    
    • .slider作为最外层包裹,里面有两个元素:一个是包含所有.slider-item的.slider-group、另一个是包含所有.dot的.dots
    • 为了使该组件更灵活,组件并不限定滑动元素必须是img,而是提供了一个插槽slot,你可以放置所有你想放置的元素,比如div、p等
    • 为.slider-group添加ref="sliderGroup"是为了通过this.$refs.sliderGroup.children获取所有轮播元素的dom,以给它们都添加"slider-item"类
    • 为.slider添加ref="slider"是为了获取父容器.slider的宽度,这样才能设置每一个轮播元素.slider-item的宽度与父容器的相等,继而能计算出包裹它们的.slider-group的宽度(累加和)

    二、js部分

    js代码100余行,全部展开不利于浏览,所以先看整体结构:

    <script type="text/ecmascript-6">
      import BScroll from 'better-scroll'
      import {addClass} from 'common/js/dom'
    
      export default {
        name: 'slider',
        props: {...},
        data () {...},
        mounted () {...},
        methods: {...},
        destoryed () {...}
      }
    </script>
    
    • 首先安装better-scrollcnpm i better-scroll -S
    • 引入better-scroll以及专题【三】中dom.js里的addClass方法
    • name: 'slider':意味着使用方法为<slider></slider>
    props
    props: {
      loop: {
        type: Boolean,
        default: true
      },
      autoPlay: {
        type: Boolean,
        default: true
      },
      interval: {
        type: Number,
        default: 4000
      }
    },
    
    • loop:是否循环播放?默认:是
    • autoPlay:是否自动播放?默认:是
    • interval:切换时间间隔,单位:毫秒,默认:4000
    data
    data () {
      return {
        dots: [],
        currIndex: 0
      }
    },
    
    • dots:轮播图组件上的小圆点,数组长度等于元素.slider-item的个数
    • currIndex:表示当前播放的是第几张,通过匹配循环索引,给当前项的小圆点添加active类(变成长条圆角状)
    mounted
    mounted () {
      setTimeout(() => {
        this._setSliderWidth()
        this._initDots()
        this._initSlider()
        if (this.autoPlay) {
          this._play()
        }
      }, 20) // 浏览器刷新一般需要17毫秒
    
      window.addEventListener('resize', () => {
        if (!this.slider) {
          return false
        }
        this._setSliderWidth(true)
        this.slider.refresh()
      })
    },
    
    • 由于浏览器刷新通常花费17毫秒,所以为了保证dom成功渲染,需要在mounted钩子中,设置一个20毫秒的延时,20毫秒之后,再执行:
      • _setSliderWidth():获取轮播图宽度
      • _initDots():初始化小圆点
      • _initSlider():初始化轮播图
      • _play():如果允许自动播放就执行播放
    • 监听window的resize事件,发现窗口尺寸变化,就重新设置轮播图宽度为当前窗口宽度、调用better-scroll组件的refresh方法
    methods
    methods: {
      _setSliderWidth (isResize) {...},
      _initDots () {...},
      _initSlider () {...},
      _play () {...}
    }
    
    _setSliderWidth()
    // 获取轮播图宽度
    _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
      }
      // 如果是循环播放,一头一尾需要多放一个dom
      if (this.loop && !isResize) {
        width += 2 * sliderWidth
      }
      this.$refs.sliderGroup.style.width = width + 'px'
    },
    
    • this.$refs.sliderGroup.children获取所有轮播元素的dom,给它们添加"slider-item"类
    • this.$refs.slider.clientWidth获取父容器的宽度sliderWidth(实际上是视窗宽度)
    • 设置.slider-item的宽度等于sliderWidth
    • 设置.slider-group的宽度等于所有.slider-item宽度累加的和
    _initDots()
    // 初始化小圆点
    _initDots () {
      this.dots = new Array(this.children.length)
    },
    
    • new一个this.children.length长度的数组,我们只关心this.dots的长度,至于数组的每一项是空(empty)还是有值,都不影响小圆点渲染
    _initSlider()
    // 初始化轮播图
    _initSlider () {
      this.slider = new BScroll(this.$refs.slider, {
        scrollX: true,
        scrollY: true,
        momentum: false, // 惯性
        snap: true,
        snapLoop: this.loop,
        snapThreshold: 0.3,
        snapSpeed: 400
      })
      this.slider.on('scrollEnd', () => {
        let pageIndex = this.slider.getCurrentPage().pageX
        if (this.loop) {
          pageIndex -= 1
        }
        this.currIndex = pageIndex
    
        if (this.autoPlay) {
          clearTimeout(this.timer)
          this._play()
        }
      })
    },
    
    • new BScroll:创建一个better-scroll的实例

      • 第一个参数是一个原生的 DOM 对象。如果传递的是一个字符串,better-scroll 内部会尝试调用 querySelector 去获取这个 DOM 对象,所以初始化代码也可以是这样:let scroll = new BScroll('.wrapper')
      • 第二个参数是一个对象,包含了一些配置,详见文档
    • getCurrentPage().pageX:在横轴方向上获取当前页面索引pageIndex

    _play()
    // 播放
    _play () {
      let index = this.currIndex + 1
      if (this.loop) {
        index += 1
      }
      this.timer = setTimeout(() => {
        this.slider.goToPage(index, 0, 400)
      }, this.interval)
    }
    
    • 该函数设置了一个计时器,在规定的时间内(this.interval)执行一次BScroll对象的goToPage方法
      • 参数一: x 横轴的页数
      • 参数二: y 纵轴的页数
      • 参数三: 动画执行的时间
    destoryed
    destoryed () {
      clearTimeout(this.timer)
    }
    

    三、css部分

    <style lang="stylus" rel="stylesheet/stylus">
      // variable.styl传送门:https://wy310.cn/2020/01/11/vue-build-basic-style-structure/
      @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>
    
    • 通过@import引入外部公用css:variable.styl
    • .slider-item被设置了左浮动,从而使所有滑动元素横向排成一排
    • .slider-group规定子元素不换行

    相关文章

      网友评论

        本文标题:【四】轮播图组件

        本文链接:https://www.haomeiwen.com/subject/eyszqhtx.html