基于vue封装的移动端swiper组件

2022-02-25 19:42:51 浏览数 (1)

基于vue封装的移动端swiper组件

直接上代码!

App.vue

代码语言:javascript复制
<template>
  <div>
    <div class="container">
      <h2>移动端轮播图</h2>
      <div v-if="imgList.length>0">
        <Swiper :delay="delay" :duration="duration" :moveRatio="moveRatio">
          <swiper-item v-for="(item,index) in imgList" :key="index">
            <img :src="item.img" alt height="200" />
          </swiper-item>
        </Swiper>
      </div>
    </div>

  </div>
</template>

<script>
// 移动端轮播图
import { Swiper, SwiperItem } from "./components/SwiperApp/index";


export default {
  name: "App",
  components: {
    Swiper,
    SwiperItem,
  },
  data() {
    return {
      imgList: [],
      mode: "vertical", // 轮播模式,默认:horizontal,可选:vertical
      delay: 1500, // 轮播间隔,默认1000
      duration: 400, // 动画时长,默认300
      moveRatio: 0.2, // 触控比率,默认0.3
    };
  },
  created() {},
  mounted() {
    fetch("/api/article/page?type=前端&nowpage=2&pagesize=5")
      .then((response) => response.json())
      .then((data) => {
        this.imgList = data.pagelist.rows;
      })
      .catch((e) => console.log("Oops, error", e));
  },
};
</script>

<style scoped>
.container {
  width: 100%;
  margin: 100px auto 0;
  box-shadow: 0 0 4px 1px rgba(232, 237, 250, 0.5);
  border: 1px solid #ebebeb;
  border-radius: 3px;
  padding: 20px;
  box-sizing: border-box;
}
</style>

Swiper.vue

代码语言:javascript复制
<template>
  <div>
    <div class="carousel" ref="carousel">
      <div
        class="panels"
        ref="panels"
        @touchstart="touchStart"
        @touchmove="touchMove"
        @touchend="touchEnd"
      >
        <slot></slot>
      </div>
      <!-- <div class="arrow">
        <i class="left-arrow"></i>
        <i class="right-arrow"></i>
      </div>-->
      <ul class="poins" ref="poins" v-if="allcount !=null">
        <li v-for="(item,index) in allcount" :key="index" :class="{active: index == currentIndex}">
          <div></div>
        </li>
      </ul>
    </div>
  </div>
</template>

<script>
import SwiperItem from "./SwiperItem";
export default {
  name: "Swiper",
  components: {
    SwiperItem,
  },
  props: {
    // 轮播间隔
    delay: {
      type: Number,
      default: 2000,
    },
    // 动画时长
    duration: {
      type: Number,
      default: 300,
    },
    // 触控比率
    moveRatio: {
      type: Number,
      default: 0.3,
    },
  },
  data() {
    return {
      allcount: null,
      animation: null,
      swiperWidth: null,
      currentIndex: 0,
      startX: 0,
      scrolling: false,
    };
  },

  created() {},
  mounted() {
    var panels = this.$refs.panels;
    this.swiperWidth = panels.offsetWidth;
    var poins = this.$refs.poins;
    this.allcount = panels.childNodes.length;
    panels.style.transform = `translate3d(-${this.swiperWidth}px, 0, 0)`;
    // panels.firstChild.classList.add("active");
    // 在刚页面加载完成的时候,this.$refs.poins是没有子元素的,
    // 因为allcount是页面加载完成后才获取的,有allcount后,才会开始进行v-for渲染dom元素,
    // 因此获取到allcount后,要等到dom更新完成了才能获取firstChild
    this.$nextTick(() => {
      // DOM更新了才执行
      this.$refs.poins.firstChild.classList.add("active");
      this.init();
    });
  },
  methods: {
    touchStart: function (e) {
      // 停止定时器
      clearInterval(this.timer);

      // 保存开始滚动的位置
      this.startX = e.touches[0].pageX;
    },

    touchMove: function (e) {
      // 1.计算出用户拖动的距离
      let currentX = e.touches[0].pageX;
      // 移动的距离
      this.moveDistance = currentX - this.startX;
      let currentPosition = -(this.currentIndex   1) * this.swiperWidth;
      let position = this.moveDistance   currentPosition;
      // 2.设置当前的位置
      this.$panels.style.transform = `translate3d(${position}px, 0, 0)`;
    },

    touchEnd: function (e) {
      let lastDistance = Math.abs(this.moveDistance);
      if (lastDistance == 0) {
        return;
      } else if (
        this.moveDistance > 0 &&
        lastDistance > this.swiperWidth * this.moveRatio
      ) {
        // 右边移动超过this.moveRatio
        // console.log("右边移动超过this.moveRatio");
        this.pre();
      } else if (
        this.moveDistance < 0 &&
        lastDistance > this.swiperWidth * this.moveRatio
      ) {
        // 左边移动超过this.moveRatio
        // console.log("左边移动超过this.moveRatio");
        this.next();
      } else {
        if (this.moveDistance > 0) {
          // console.log("右边移动不超过0.3");
          if (this.currentIndex   1 == this.allcount) {
            let position = -this.swiperWidth * (this.currentIndex   1);
            this.animate(position);
          } else {
            this.currentIndex  ;
            this.pre();
          }
        } else {
          // console.log("左边移动不超过0.3");
          this.currentIndex--;
          this.next();
        }
      }
      this.loopStart();
    },
    // init
    init() {
      this.timer = null;
      this.$root = this.$refs.carousel;
      this.$panels = this.$root.querySelector(".panels");
      this.$panelsItem = this.$root.querySelectorAll(".panels div");
      // this.$poins = this.$root.querySelectorAll(".poins li");
      // 3.如果大于1个, 那么在前后分别添加一个slide
      if (this.allcount > 1) {
        let cloneFirst = this.$panelsItem[0].cloneNode(true);
        let cloneLast = this.$panelsItem[this.allcount - 1].cloneNode(true);
        this.$panels.insertBefore(cloneLast, this.$panelsItem[0]);
        this.$panels.appendChild(cloneFirst);
      }
      const css = ($node, newStyle) => Object.assign($node.style, newStyle);
      this.loopStart();
      // this.bindEvent();
    },
    // 绑定事件
    bindEvent() {
      this.$next.onclick = this.next.bind(this);
      this.$pre.onclick = this.pre.bind(this);
      // 循环轮播
      this.$root.onmouseover = () => clearInterval(this.timer);
      this.$root.onmouseleave = () => this.loopStart();
    },
    // 下一个
    next() {
      this.scrolling = true;
      let currentIndex = this.currentIndex;
      let nextIndex = (this.currentIndex   1) % this.$panelsItem.length;
      this.currentIndex = nextIndex;
      if (currentIndex <= this.allcount) {
        let position = -this.swiperWidth * (currentIndex   1   1);
        this.animate(position);
      }
      if (currentIndex   1 == this.allcount) {
        setTimeout(() => {
          let position = -this.swiperWidth;
          this.$panels.style.transform = `translate3d(${position}px, 0, 0)`;
        }, this.duration);
      }
      setTimeout(() => {
        this.scrolling = false;
      }, this.duration);
    },
    // 上一个
    pre() {
      this.scrolling = true;
      let currentIndex = this.currentIndex;
      let preIndex =
        (this.currentIndex - 1   this.$panelsItem.length) %
        this.$panelsItem.length;
      this.currentIndex = preIndex;

      // pre = 3
      if (preIndex == this.allcount - 1) {
        this.animate(0);
        setTimeout(() => {
          let position = -this.swiperWidth * this.allcount   1;
          this.$panels.style.transform = `translate3d(${position}px, 0, 0)`;
        }, this.duration);
      } else {
        // pre =012
        let position = -this.swiperWidth * (preIndex   1);
        this.animate(position);
      }
      setTimeout(() => {
        this.scrolling = false;
      }, this.duration);
    },
    // 动画
    animate(position) {
      this.$panels.style.transition = `transform ${this.duration}ms ease 0s`;
      this.$panels.style.transform = `translate3d(${position}px, 0, 0)`;
      setTimeout(() => {
        this.$panels.style.transition = `all 0ms ease 0s`;
      }, 100);
    },
    // 开始轮播
    loopStart() {
      this.timer = setInterval(() => {
        this.$panels.style.transform = `translate3d(-${this.swiperWidth}px, 0, 0)`;
        this.next();
      }, this.delay);
    },
  },
};
</script>

<style scoped>
.carousel {
  position: relative;
  text-align: center;
  overflow: hidden;
  z-index: 30;
}
.carousel .panels {
  display: flex;
  width: 100%;
  height: 100%;
}
.carousel .panels .active {
  z-index: 10;
}
.carousel .poins {
  position: absolute;
  margin: 0;
  padding: 0;
  bottom: 0;
  left: 50%;
  transform: translate(-50%);
  z-index: 999;
}
.carousel .poins li {
  display: inline-block;
  list-style: none;
  padding: 0 5px;
  z-index: 99;
}
.carousel .poins li div {
  display: block;
  opacity: 0.1;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background-color: rgba(10, 10, 10);
  transition: 0.3s;
}
.carousel .poins li.active div {
  opacity: 0.5;
}
</style>

SwiperItem.vue

代码语言:javascript复制
<template>
  <div class="panels-item">
    <slot></slot>
  </div>
</template>

<script>
export default {
  name: "SwiperItem",
  components: {},
  data() {
    return {};
  },
  created() {},
  mounted() {},
};
</script>

<style scoped>
.panels-item {
  flex-shrink: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
  width: 100%;
  z-index: 10;
}
.panels-item img {
  width: 100%;
}
</style>

0 人点赞