Vue 组件封装之 Carousel 轮播图

Vue 组件封装之 Carousel 轮播图

  • 一、Carousel 轮播图组件
  • 二、使用案例
  • 三、API 使用指南
  • 四、源代码

一、Carousel 轮播图组件

组件说明:
实现无缝轮播。

效果展示:
无缝轮播效果
Vue 组件封装之 Carousel 轮播图_第1张图片
实现的功能:

  1. 是否自动轮播,默认为是,可配置;
  2. 是否显示指示灯,是否显示左右点击按钮,默认为是,可配置。
    (左右点击按钮是点击图片区域(移动端)或者鼠标移动到图片区域(PC端)出现)
  3. 指示灯点击轮播,左右按钮点击轮播;
  4. 左右滑动轮播。

二、使用案例

使用div:

<template>
  <el-carousel-img>
     <div style="background: pink;font-size:3rem;text-align: center">1</div>
     <div style="background: yellow;font-size:3rem;text-align: center">2</div>
  </el-carousel-img>
</template>

使用图片:

<template>
  <el-carousel-img>
    <img :src="carpool" alt="">
    <img :src="gold" alt="">
  </el-carousel-img>
</template>

<script>
  import carpool from '../assets/carpooling.png';
  import gold from '../assets/gold-detail.png';
  export default {
    data(){
          return{
            carpool,
            gold
          }
    }
  }
</script>

三、API 使用指南

属性 说明 类型 默认值
height 轮播图高度,注意,如果设置了指定高度,图片也要设置高度 String 200px
interval l轮播间隔时间 Number 2000ms
transitionTime 轮播过度时间,必须小于轮播间隔时间 Number 1000ms
isIndicator 是否显示指示灯 Boolean true
isHandle 是否显示左右按钮 Boolean true
autoplay 是否自动播放 Boolean true

四、源代码

CarouselImg.vue

<template>
  <div class="carousel-container" :style="{height:height}"
       @mouseenter.stop="handleMouseEnter"
       @mouseleave.stop="handleMouseLeave"
  >
    <div v-if="handleDisplay">
    <button
      class="carousel-arrow carousel-arrow-left"
      @click.stop="throttledArrowClick(controlDisabled[0].label)"
      :disabled="controlDisabled[0].disabled"
    ><i class="arrow-left"></i></button>
    <button
      class="carousel-arrow carousel-arrow-right"
      @click.stop="throttledArrowClick(controlDisabled[1].label)"
      :disabled="controlDisabled[1].disabled"
    ><i class="arrow-right"></i></button>
    </div>
    <div ref="list" class="carousel-list"
         @touchstart="moveStart"
         @touchmove="moving"
         @touchend="moveEnd"
    >
      <slot class="carousel-pane"></slot>
    </div>
    <ul class="carousel-indicator" v-if="isIndicator" ref="indicator">
      <li v-for="(item,index) in carouselLength"
          @click="setCurrentItem(index)"
          :class="[index == (currentIndex-1)?'is-active-item':'','carousel-indicator-item']">
      </li>
    </ul>
  </div>
</template>
<script>
  export default {
    name: "ElCarouselImg",
    data(){
      return {
         currentIndex:1,//当前索引
         carouselLength:0,//实际轮播图个数
         timer:null,//定时器
         handle:false,//是否显示左右点击按钮
        //设置点击状态
        controlDisabled:[
          {label:'leftBtn',disabled:false},
          {label:'rightBtn',disabled:false},
          {label:'child',disabled:false},
        ],
        isMoving:false,
        start: {
          x: 0
        },
        time:0,//滑动时触摸结束回弹时间。
        imgList:null,//轮播图片的父元素
        width:window.innerWidth//移动距离
      }
    },
    props:{
      interval: {
        type: Number,
        default: 2000//轮播间隔时间
      },
      transitionTime:{
        type: Number,
        default: 1000//轮播过度时间
      },
      isIndicator: {
        type: Boolean,
        default: true//是否显示指示灯
      },
      height: String,//轮播图高度
      isHandle:{
        type: Boolean,
        default: true//是否显示左边点击按钮,默认点击图片并且图片大于1张时显示
      },
      isAutoplay:{
        type: Boolean,
        default: true//是否开启自动轮播
      }
    },
    computed: {
      handleDisplay() {
        return this.isHandle && this.handle && this.carouselLength>1;
      },
    },
    mounted(){
      //启动轮播图
      this.prepare();
    },
    created(){
    },
    methods:{
      moveStart(e){
        e.stopPropagation();
        this.start.x = e.changedTouches[0].pageX;
      },
      moving(e){
        e.stopPropagation();
        this.pauseTimer();
        var distanceX = e.changedTouches[0].pageX - this.start.x;
        this.imgList.style.transform ='translate3d('+ (-this.currentIndex * this.width + distanceX) +'px,0,0)';
        this.imgList.style.transition = 'transform 0s';
        this.isMoving = true;
      },
      moveEnd(e){
          if(this.isMoving){
            e.stopPropagation();
            this.time = 300;
            var distanceX = e.changedTouches[0].pageX - this.start.x;
            if(distanceX<0){
              this.currentIndex++ ;
              if(this.currentIndex == this.carouselLength+1){
                this.criticality(1,undefined,300);
              }else {
                this.play(300);
              }
            }else{
              this.currentIndex-- ;
              if(this.currentIndex == 0){
                this.criticality(this.carouselLength,undefined,300);
              }else {
                this.play(300);
              }
            };
            this.isMoving = false;
            this.autoPlay();
          }
      },
        prepare(){
          this.imgList = this.$refs.list;
          var children = this.imgList.children;
          this.carouselLength = children.length;
          if(this.carouselLength>1){
            this.width = this.imgList.clientWidth;
            //首尾各添加一张图片,以便无缝轮播
            var firstDom = this.imgList.firstElementChild && this.imgList.firstElementChild.cloneNode(true);
            var lastDom = this.imgList.lastElementChild && this.imgList.lastElementChild.cloneNode(true);
            if(this.imgList.firstElementChild){
              this.imgList.appendChild(firstDom);
              this.imgList.insertBefore(lastDom,this.imgList.firstElementChild);
              this.imgList.style.transform ='translate3d('+ (-this.width) +'px,0,0)';
            }
            if(this.isAutoplay){
              this.autoPlay();
            }
          }
        },
      //停止定时器
      pauseTimer(){
          if(this.timer){
            clearTimeout(this.timer);
            this.timer = null;
          }
      },
      handleMouseEnter(){
          if(this.isHandle){
            this.handle = true;
            this.pauseTimer();
          }
      },
      handleMouseLeave(){
        if(this.isHandle||this.handle){
          this.handle = false;
          this.autoPlay();
        }
      },
      throttledArrowClick(dir){
          if(dir == 'leftBtn'){
            this.currentIndex -- ;
          }else {
            this.currentIndex ++ ;
          };
        if(this.currentIndex == 0){
          this.criticality(this.carouselLength,this.controlDisabled[0]);
        }else if(this.currentIndex == (this.carouselLength+1)){
          this.criticality(1,this.controlDisabled[1]);
        }else {
          this.play();
        }
      },
      //轮播处理逻辑
      play(transitionTime){
        this.imgList.style.transform ='translate3d('+ (-this.currentIndex * this.width)+'px,0,0)';
        if(transitionTime){
          this.imgList.style.transition =  'transform '+transitionTime+ 'ms';
        }else {
          this.imgList.style.transition = 'transform '+this.transitionTime+ 'ms';
        }
      },
      setCurrentItem(index){
         //点击指示灯时停止轮播。
         this.pauseTimer();
        if(this.controlDisabled[2].disabled){
          return;
        }
         //点击第一张时
         if(index == 0 && this.currentIndex == (this.carouselLength)){
             this.currentIndex++;
             this.criticality(1,this.controlDisabled[2]);
         }else if(index == (this.carouselLength-1) && this.currentIndex == 1){
           this.currentIndex -- ;
           this.criticality(this.carouselLength,this.controlDisabled[2]);
         }else {
           this.currentIndex = index+1;
           this.play();
         }
      },
      criticality(boundary,item,transitionTime){
        this.play(transitionTime);
        this.currentIndex = boundary;
        setTimeout(()=>{
          this.imgList.style.transform ='translate3d('+ (-this.width*boundary)+'px,0,0)';
          this.imgList.style.transition =  'transform 0ms';
        },transitionTime?transitionTime:this.transitionTime);
        //点击临界值时将点击状态暂停掉以便偷梁换柱
        if(item){
              var after = new Date().getTime()+1000+100;
              if((new Date().getTime())<= after){
              item.disabled = true;
                setTimeout(()=>{
                  item.disabled = false;
                },after-new Date().getTime())
            }
        }
      },
      autoPlay(){
          this.timer = setInterval(()=>{
            this.currentIndex++;
            if(this.currentIndex == this.carouselLength+1){
              clearInterval(this.timer);
              this.criticality(1);
              this.autoPlay();
            }else {
              this.play();
            }
          },this.interval);
      }
    },
    beforeDestroy() {
      this.pauseTimer();
    }
  }
</script>
<style>
  .carousel-container{
    position: relative;
    width:100%;
    height: 200px;
    overflow: hidden;
  }
  .carousel-indicator{
    display: flex;
    bottom: 10%;
    position: absolute;
    z-index: 99;
    width: 100%;
    justify-content: center;
  }
  .is-active-item{
    opacity: 1!important;
  }
  .carousel-indicator-item{
    margin: 0 5px;
    background: #fff;
    width: 30px;
    height: 3px;
    list-style: none;
    border-radius: 20px;
    opacity: 0.5;
  }
  .carousel-list{
    display: flex;
    position: relative;
  }
  .arrow-left,.arrow-right{
    font-family: element-icons;
    font-style: normal;
  }
  .arrow-right:before {
    content: "\e6e0"
  }
  .carousel-list img{
    width: 100%;
    flex-shrink: 0;
  }
  .carousel-list div{
    width: 100%;
    flex-shrink: 0;
    overflow: hidden;
  }
  .arrow-left:before {
    content: "\e6de"
  }
  .carousel-arrow {
    border: none;
    outline: 0;
    padding: 0;
    margin: 0;
    height: 36px;
    width: 36px;
    cursor: pointer;
    -webkit-transition: .3s;
    transition: .3s;
    border-radius: 50%;
    background-color: rgba(31, 45, 61, .11);
    color: #FFF;
    position: absolute;
    top: 50%;
    z-index: 10;
    -webkit-transform: translateY(-50%);
    transform: translateY(-50%);
    text-align: center;
    font-size: 12px
  }
  .carousel-arrow-left{
    left: 15px;
  }
  .carousel-arrow-right{
    right: 15px;
  }
</style>

你可能感兴趣的:(#,Vue通用组件封装)