1. 程式人生 > >vue專案中用better-scroll製作輪播圖

vue專案中用better-scroll製作輪播圖

主要html架構

 <!- slider元件 ->
<div class="slider-wrapper" ref="sliderWrapper">
   <div class="slider-ul" ref="sliderUl">
     <div class="slider-li" v-for="(item,index) in imgDatas">
       <a href="#">
         <img src="#">
       </a>
     </div>
   </div>
</div>

解釋:

  1. slider-wrapper是外層容器,它的寬度是更外層容器的寬度,比如在父元件中
<!- 父元件 ->
<div class="parent-box">
	<!- slider-wrapper繼承,撐滿parent-box的寬度 -->
	<slider></slider> 
</div>
  1. slider-ul是圖片組ul的寬度,寬度是slider-li的數量 * slider-li的width
  2. slider-li是一個圖片項
  3. imgDatas是圖片的資料陣列

主要CSS程式碼

/* slider元件的主要CSS程式碼 */

.slider-wrapper {
  position: relative;
  width: 100%;   /*撐滿父容器*/
  height: 100%;  /*撐滿父容器*/
  min-height: 1px;
  overflow: hidden;
}

.slider-ul { 
  /* mounted的時候設定ul的寬度,設定方法是100% x 多少個圖片item,如5個圖片項則是500% */
  /* 如果是假資料進行樣式設定除錯,可以先設定width為多少個100%  */
  position: relative;
  white-space: nowrap;
  overflow: hidden;   /*清除浮動*/
  height: 100%;
}

.slider-li {
  /* 同樣是 mounted時設定一個item的寬度,寬度要和slider-wrapper的寬度一樣,則 500% * 0.2 = 100% */
  /* 同樣是假資料的樣式除錯的時候,可以先設定width,如slider-wrapper為100%,slider-ul為500%,即有五張圖片,此時設定slider-li的width為20%,500 * 0.2 = 100,則和slider-wrppaer寬度一致 */
  height: 100%;
  float: left;   /*要設定浮動*/
  box-sizing: border-box;
  overflow: hidden;
}

.slider-li a {
  overflow: hidden;
  width: 100%;
  height: 100%;
  display: block;
}

.slider-li img {
  width: 100%;
  height: 100%;
  display: block;
}

元件主要部分

  1. 安裝:npm install better-scroll --save
  2. 在slider元件中引入:import BScroll from ‘better-scroll’
// props,父元件傳入子元件的引數
props: {
  loop: {      //是否迴圈播放
    type: Boolean,
    default: true
  },
  autoPlay: {  //是否自動播放
    type: Boolean,
    default: true
  },
  interVal: {  //輪播一張圖片的時間
    type: Number,
    default: 3000
  },
  imgDatas: {  //傳入的imgDatas資料
    type: Array,
    default: []
  }
}
//主要內建資料
data() {
  return {
    currentPageIndex: 0  //記錄當前是哪一張輪播圖圖片,下標從0開始,即第一張
  }
}
//methods
methods:{
    //第一步,根據傳入的imgDatas初始化slider-ul,slider-li的寬度,為百分比
    //注意,better-scroll在初始化時,當你設定了loop的時候,會在前後自動增加2張圖片,例如5張圖片會變成7張
    //由於這是第一步,暫時沒有初始化better-scroll,所以獲取children的時候,只是5張圖片,props的loop為true時,要加多兩個,然後設定ul,li的寬度
    _setUlWidth() {
      this.children = this.$refs.sliderUl.children
      let childrenLength = this.loop ? this.children.length + 2 : this.children.length
      this.$refs.sliderUl.style.width = childrenLength * 100 + '%'
      for (var i = 0; i < this.children.length; i++) {
        this.children[i].style.width = 100 / childrenLength + '%'
      }
    },
    
    //第二步,初始化better-scroll
    _initSlider() {
      let that = this
      this.slider = new BScroll(this.$refs.sliderWrapper, {
        scrollX: true,
        scrollY: false,
        momentum: false,
        snap: {
          loop: this.loop,
          threshold: 0.3,
          speed: 400
        },
        probeType: 2
      })
      //監聽scroll結束的時候,賦值更新currentPageIndex
      this.slider.on('scrollEnd', () => {
        clearInterval(that.timer)
        that.currentPageIndex = that.slider.getCurrentPage().pageX
        let childrenLength = this.loop ? this.children.length - 2 : this.children.length
        that._setAutoplay()
      })
      let childrenLength = this.loop ? this.children.length - 2 : this.children.length
      //無縫
      this.checkScroll(childrenLength,this.$refs.sliderWrapper.offsetWidth)
    },
    
    //第三步,設定自動播放
    //這一步不需要重新設定currentPageIndex ,因為轉到下一頁時觸發了scrollEnd事件,已經自動賦值更新
    _setAutoplay() {
      let goToIndex = this.currentPageIndex + 1
      let childrenLength = this.loop ? this.children.length - 2 : this.children.length
      //設定右滑到最後一張的無縫切換,返回到第一張
      if (goToIndex >= childrenLength) {
        goToIndex = 0
      }
      this.timer = setInterval(() => {
        this.slider.goToPage(goToIndex, 0, 400)
      }, this.interVal)
    },
    
    //監聽滑事件
    checkScroll(childrenLength,itemWidth) {
      let that = this
      this.slider.on('scroll', () => {
        clearInterval(that.timer)
        let leftScroll = parseInt(that.$refs.sliderUl.style['transform'].substring(10, 15))
        let left = -1 * itemWidth
        let right = left * childrenLength
        if (leftScroll >left) {
          that.slider.goToPage(childrenLength, 0, 400)
        }else if(leftScroll < right) {
          that.slider.goToPage(1, 0, 400)
        }
      })
    }
},
mounted() {
    setTimeout(() => {
      this._setUlWidth()
      this._initSlider()
      this._setAutoplay()
    }, 20)
    window.addEventListener('resize', () => {
      if (!this.slider) {
        return
      }
      this._setUlWidth()
    })
}

這樣已經可以輪播了哦,更詳細的樣式和功能程式碼看下面的原始碼


原始碼
使用說明:

<div style="width:400px;height:280px;">
	<slider :imgDatas="imgDatas"></slider>
</div>
imgDatas: [
 { imgSrc: require("./images/1.jpg"), linkUrl: '#' },
 { imgSrc: require("./images/2.jpg"), linkUrl: '#' },
 { imgSrc: require("./images/3.jpg"), linkUrl: '#' },
 { imgSrc: require("./images/4.jpg"), linkUrl: '#' },
]
<template>
  <div class="slider-wrapper" ref="sliderWrapper">
    <div class="slider-ul" ref="sliderUl">
      <div class="slider-li" v-for="(item,index) in imgDatas" :key="index">
        <a :href="item.linkUrl">
          <img :src="item.imgSrc">
        </a>
      </div>
    </div>
    <div class="dots">
      <span 
        :key="'b'+index" 
        v-for="(item,index) in imgDatas" 
        :class="{'dot-active' : currentPageIndex == index}"
        @click="linkToPage(index)"
      ></span>
    </div>
    <div class="arrow left-arrow" @click="scrollbtnEvent('left')">
      <span class="iconfont icon-xiangzuo"></span>
    </div>
    <div class="arrow right-arrow" @click="scrollbtnEvent('right')">
      <span class="iconfont icon-xiangyou-copy"></span>
    </div>
  </div>
</template>

<script>
import BScroll from 'better-scroll'
export default {
  props: {
    loop: {
      type: Boolean,
      default: true
    },
    autoPlay: {
      type: Boolean,
      default: true
    },
    interVal: {
      type: Number,
      default: 3000
    },
    imgDatas: {
      type: Array,
      default: []
    }
  },
  data() {
    return {
      currentPageIndex: 0
    }
  },
  mounted() {
    setTimeout(() => {
      this._setUlWidth()
      this._initSlider()
      this._setAutoplay()
    }, 20)
    window.addEventListener('resize', () => {
      if (!this.slider) {
        return
      }
      this._setUlWidth()
    })
  },
  methods: {
    //初始化wrapper的寬度
    _setUlWidth() {
      this.children = this.$refs.sliderUl.children
      let childrenLength = this.loop ? this.children.length + 2 : this.children.length
      this.$refs.sliderUl.style.width = childrenLength * 100 + '%'
      for (var i = 0; i < this.children.length; i++) {
        this.children[i].style.width = 100 / childrenLength + '%'
      }
    },
    //播放
    _setAutoplay() {
      let goToIndex = this.currentPageIndex + 1
      let childrenLength = this.loop ? this.children.length - 2 : this.children.length
      if (goToIndex >= childrenLength) {
        goToIndex = 0
      }
      this.timer = setInterval(() => {
        this.slider.goToPage(goToIndex, 0, 400)
      }, this.interVal)
    },
    //初始化better-scroll
    _initSlider() {
      let that = this
      this.slider = new BScroll(this.$refs.sliderWrapper, {
        scrollX: true,
        scrollY: false,
        momentum: false,
        snap: {
          loop: this.loop,
          threshold: 0.3,
          speed: 400
        },
        probeType: 2
      })
      //監聽scroll結束的時候,賦值更新currentPageIndex
      this.slider.on('scrollEnd', () => {
        clearInterval(that.timer)
        that.currentPageIndex = that.slider.getCurrentPage().pageX
        let childrenLength = this.loop ? this.children.length - 2 : this.children.length
        that._setAutoplay()
      })
      let childrenLength = this.loop ? this.children.length - 2 : this.children.length
      //左滑到最左還想輪播
      this.checkScroll(childrenLength,this.$refs.sliderWrapper.offsetWidth)
    },
    //監聽滑事件
    checkScroll(childrenLength,itemWidth) {
      let that = this
      this.slider.on('scroll', () => {
        clearInterval(that.timer)
        let leftScroll = parseInt(that.$refs.sliderUl.style['transform'].substring(10, 15))
        let left = -1 * itemWidth
        let right = left * childrenLength
        if (leftScroll >left) {
          that.slider.goToPage(childrenLength, 0, 400)
        }else if(leftScroll < right) {
          that.slider.goToPage(1, 0, 400)
        }
      })
    },
    //向左向右
    scrollbtnEvent(type) {
      clearInterval(this.timer)
      let childrenLength = this.loop ? this.children.length - 2 : this.children.length
      let goIndex = (type == 'left') ? this.currentPageIndex - 1 : this.currentPageIndex + 1
      if (type == 'left') {
        if (goIndex < 0) {
          goIndex = childrenLength-1
        }
      } else {
        if (goIndex >= childrenLength) {
          goIndex = 0
        }
      }
      this.slider.goToPage(goIndex, 0, 400)
    },
    //按小圓圈
    linkToPage(index) {
      this.slider.goToPage(index, 0, 400)
    }
  }
}
</script>

<style>
.slider-wrapper {
  position: relative;
  width: 100%;
  height: 100%;
  min-height: 1px;
  overflow: hidden;
}
.dots {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  bottom: 0.1rem;
  display: flex;
  flex-direction: row;
}
.dots span {
  cursor: pointer;
  display: block;
  width: 0.1rem;
  height: 0.1rem;
  border-radius: 0.05rem;
  background: #CCCCCC;
  margin-right: 0.05rem;
}
.dot-active {
  width: 0.2rem !important;
  background: #fff !important;
}
.arrow {
  display: flex;
  align-items: center;
  position: absolute;
  top: 0;
  cursor: pointer;
  color: #333;
  opacity: 0.4;
  font-size: 0.3rem;
  width: 0.7rem;
  height: 100%;
}
.arrow:hover {
  opacity: 1;
  background-image: linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);
  color: #fff;
}
.left-arrow {
  left: 0;
}
.left-arrow span {
  margin-left: 0.2rem;
}
.right-arrow {
  justify-content: flex-end;
  right: 0;
}
.right-arrow span {
  margin-right: 0.2rem;
}

.slider-ul {
  position: relative;
  white-space: nowrap;
  overflow: hidden;
  height: 100%;
}
.slider-li {
  /* width: 20%; */
  height: 100%;
  float: left;
  box-sizing: border-box;
  overflow: hidden;
}
.slider-li a {
  overflow: hidden;
  width: 100%;
  height: 100%;
  display: block;
}
.slider-li img {
  width: 100%;
  height: 100%;
  display: block;
}
@media screen and ( max-width : 500px  ) { 
  .arrow{
    display: none;
  }
}
</style>