【四】轮播图组件

关于专题【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规定子元素不换行
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容

  • Vue-Music 一| 前期工作 1.项目初始化 npm install -g vue-cli vue init...
    noobakong阅读 1,818评论 0 5
  • 最近在跟着视频补习Vue。仿QQ音乐上的轮播图。用了better-scroll HTML部分,主要是三个部分。sl...
    考古羊阅读 5,153评论 1 1
  • 在base/slider文件夹下创建slider.vue文件并在recommend.vue文件中引入 slider...
    xu丽_twilight阅读 164评论 0 0
  • 以前在我家住了很长时间的一个女孩,因为家里地方少,有四个哥哥,我妈见是老邻居的女儿,就让她在我家住,反正有空房间。...
    雨林木丰阅读 490评论 1 4
  • 这是金伊恩原创的第1篇文章 创业两年,公司仍然亏损。 回头看,走过的那些坑都是因为自己的学识不够。 “不专业,别创...
    金伊恩阅读 351评论 0 0