1

概述

轮播的原理是每一帧都选出一个当前元素,前一个元素,后一个元素然后排成一行,最后改变这三个元素的translate来触发css3的transition进行动画,当有touch事件的时候,要实时改变各个元素的位置,所以要把transition关闭。组件demo地址 m.cm233.com

<template>
  <section class="shuffling-wrapper">
    <ul :class="['shuffling-bar',{trans : open, transnext: openNext, transpre: openPre}]">
      <template v-for="item in shufflingData">
        <li
        :class="['item', {
          pre: $index == preIndex && shufflingData.length > 2,
          next: $index == nextIndex && shufflingData.length > 2,
          current: $index == shufflingIndex}]"
        :style="translateObj[$index]"
        @touchstart="shufflingData.length > 2 && touchStart($event)"
        @touchmove.prevent="shufflingData.length > 2 && touchMove($event)"
        @touchend="shufflingData.length > 2 && touchEnd($event)">
          <a class="link" href="{{item.link}}">
            <img class="img" :src="item.img" alt="{{item.subject}}">
          </a>
        </li>
      </template>
      <li class="shuffling-btn">
        <i class="btn-item" :style="{width: btnWidth+'px', transform: 'translate3d('+(shufflingIndex*100)+'%,0,0)'}"></i>
      </li>
    </ul>
  </section>
</template>
   .shuffling-wrapper{
    width: 100%;
  }
  .shuffling-bar{
    position: relative;
    width: 100%;
    overflow: hidden;
    padding-top: 70.66%;
    .item{
      position: absolute;
      top: 0;
      bottom: 0;
      left: 0;
      right: 0;
      margin: auto;
      z-index: 100;
      user-select: none;
      -webkit-touch-callout:none;
      -webkit-tap-highlight-color: transparent;
    }
    .link{
      display: block;
      width: 100%;
      height: 100%;
    }
    .img{
      display: block;
      width: 100%;
      height: 100%;
    }

    .current{
      transform: translate3d(0, 0, 0);
      z-index: 300;
    }
    .pre{
      transform: translate3d(-100%, 0, 0);
      z-index: 300;
    }
    .next{
      transform: translate3d(100%, 0, 0);
      z-index: 300;
    }
  }
  .trans{
    .current{
        transition: transform .3s ease-in-out;
    }
  }
  .transpre{
    .pre{
        transition: transform .3s ease-in-out;
    }
  }
  .transnext{
    .next{
        transition: transform .3s ease-in-out;
    }
  }
  .shuffling-btn{
    height: 3px;
    background-color: rgba(255,255,255,.8);
    position: relative;
    z-index: 400;
    .btn-item{
      position: absolute;
      top: 0;
      left: 0;
      height: 3px;
      background-color: #e81926;
      border-radius: 1px;
      transition: transform .3s ease-in-out;
    }
  }
import {getClient} from '../vuex/getters'
  export default{
    ready: function(){ //初始化
      this.caculateIndex(); //计算初始前一个元素,当前元素,后一个元素
      this.autoScroll(); //开始自动轮播

    },
    props: {
      shufflingData: {
        type: Array,
        default: () => []
      }
    },
    data(){
      let that = this;
      return {
        shufflingIndex: 0, //当前元素的index
        nextIndex: 0, //后一个元素的序列
        preIndex: 0, //前一个元素的序列
        timer: 0, // 储存循环的计时器序号
        translateObj: {},//touch事件时用来记录移动位置并应用到style中
        open: true, // 当前元素的动画开关
        openNext: false, // 后一个元素的动画开关
        openPre: true, // 前一个元素的动画开关
        timeOut: false, // 和setTimeout一起可确保touch事件和之后的小动画完成后再执行自动轮播
        moveOpen: false,
        btnWidth: 0
      }
    },
    watch: {
      'shufflingData' : function(val){
        this.btnWidth = this.clientInfo.width/this.shufflingData.length;
        this.caculateIndex();
      }
    },
    methods: {
      touchStart(event){
        if(!this.timeOut){
        let that = this;
          that.startX = event.changedTouches[0].pageX; //初始位置
          this.open = false;
          this.openPre = false;
          this.openNext = false;
          this.moveOpen = true;
        }
      },
      touchMove(event){
        if(this.moveOpen){
          this.movingX = event.changedTouches[0].pageX; //移动中的位置
          this.percent = ((this.movingX-this.startX)/this.clientInfo.width)*100;
          //需要响应到style中的transform属性添加,必须要用$set方法,否则不会响应到视图
          this.$set('translateObj[preIndex].transform','translate3d('+(this.percent-100)+'%, 0, 0)');
          this.$set('translateObj[nextIndex].transform','translate3d('+(this.percent+100)+'%, 0, 0)');
          this.$set('translateObj[shufflingIndex].transform','translate3d('+(this.percent)+'%, 0, 0)');
        }
      },
      touchEnd(){
        if(!this.timeOut){
          this.moveOpen = false;
          this.timeOut = true;
          this.open = true;
          this.openPre = true;
          this.openNext = true;
          this.$set('translateObj[preIndex].transform','');
          this.$set('translateObj[nextIndex].transform','');
          this.$set('translateObj[shufflingIndex].transform','');
          if(this.percent <= -30){ //假如向左滑了30%,则向左移动一屏,向左移动一屏需要关掉下一个元素的动画开关,否则后后一屏的元素会飞过去
            this.sufflingChange(); //向右侧滚动(包含最后一个元素时的处理)
            this.openNext = false;
          }else if(this.percent >= 30){ //假如向右滑了30%,则向右移动一屏,向右移动一屏需要关掉前一个元素的动画开关,否则前前一屏的元素会飞过去
            if(this.shufflingIndex == 0){ //向左侧滚动(包含第一个元素的处理)
              this.shufflingIndex = this.shufflingData.length-1;
            }else{
              this.shufflingIndex--;
            }
            this.openPre = false;
            this.caculateIndex();
          }
          setTimeout(() => { //确保移动后的动画完成,延迟和动画时间设置一致
            this.autoScroll();
            this.timeOut = false;
          }, 300);
        }
      },
      autoScroll(){ //进行自动轮播
        let that = this;
        clearInterval(that.timer);
        that.openNext = false;
        that.openPre = true;
        that.timer = setInterval(that.sufflingChange,4000);
      },
      sufflingChange(){ //向右侧滚动
        if(this.shufflingIndex == this.shufflingData.length - 1){
          this.shufflingIndex = 0;
        }else{
          this.shufflingIndex++;
        }
        this.caculateIndex();
      },
      caculateIndex(){ //计算上一个元素和下一个元素的index
        this.preIndex = this.shufflingIndex - 1 < 0 ? this.shufflingData.length-1 : this.shufflingIndex - 1;
        this.nextIndex = this.shufflingIndex + 1 >= this.shufflingData.length ? 0 : this.shufflingIndex + 1;
      }
    },
    vuex:{
      getters: {
        clientInfo : getClient
      }
    }
  }

通过写这个组件对vue的数据驱动视图的思想更了解了,感觉vue和css3真是一对好基友!用起来超级舒服!

使用方法及注意事项

使用时新建一个组件,把对应部分copy进去就可以了,比如组件叫shuffling.vue, 引入时

<shuffling :shuffling-data = 'shuffling'></shuffling>
import Shuffling from '../components/Shuffling'
export default {
  data(){
    return{
      shuffling: [
       {
         link: 'www.baidu.com',
         img: 'src.alicdn.com/fdfdfd.jpg',
         subject: '233333'
       },
       {
         link: 'www.baidu.com',
         img: 'src.alicdn.com/fdfdfd.jpg',
         subject: '233333'
       },
       {
         link: 'www.baidu.com',
         img: 'src.alicdn.com/fdfdfd.jpg',
         subject: '233333'
       }
      ]
    }
  },
components: {
    Shuffling
  }
}

组件暂时还未对2个以内的数组做兼容,2个以内将不会执行动画,只有图片切换效果。轮播进度条目前是墨瞳官网所示的轮播样式。想要改一下也会非常简单,只要利用好shufflingIndex这个属性就行了。
组件中还有一个clientInfo,这个对象是浏览器宽高的对象,我的项目中是存在vuex里的,因为很多组件都会用到,如果不需要vuex的话,可以直接写在组件里。
组件的原理注释中都写的很清楚了,理解以后能更好的应用


Ganother
152 声望12 粉丝

www.ganother.com