vue3 swiper 轮播图

SwiperView.vue

<template>
  <div class="carousel" ref="carousel">
    <div
      class="carousel__wrapper"
      :style="{ transform: `translateX(${translate}px)` }"
    >
      <div v-for="(item, index) in assets" :key="index" class="carousel__item">
        <img :src="item.assetUrl" />
      </div>
    </div>

    <div class="carousel__arrow" v-if="arrow">
      <div class="prev_icon arrow_icon" @click="previousSlide">
        <img src="@/assets/swiper/arrow_icon.svg" alt="" />
      </div>

      <div class="next_icon arrow_icon" @click="nextSlide">
        <img src="@/assets/swiper/arrow_icon.svg" alt="" />
      </div>
    </div>

    <div class="carousel__indicator" v-if="indicator">
      <div class="carousel__indicator_thumbnails" ref="slideRef">
        <div
          v-for="(item, index) in assets"
          :ref="getDivDom"
          :data-id="index"
          :key="index"
          :class="{ indicator: true, active: index === currentIndex }"
          @click="jumpTo(index)"
        >
          <img :src="item.assetUrl" />
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import {
  onMounted,
  defineProps,
  ref,
  defineExpose,
  watch,
  nextTick,
} from "vue";

const props = defineProps({
  assets: {
    type: Array,
    default: () => [],
  },
  index: {
    type: Number,
    default: 0,
  },
  arrow: {
    type: Boolean,
    default: true,
  },
  indicator: {
    type: Boolean,
    default: true,
  },
});

const currentIndex = ref(props.index);
const currentAsset = ref(props.assets[currentIndex.value]);
const translate = ref(0);
const carousel = ref();
const slideRef = ref(null);
let len = 0;
let offsetWidth = 0;

const divDomList = ref(new Map());

const getDivDom = (el) => {
  if (el) {
    divDomList.value.set(parseInt(el.dataset["id"]), el);
  }
};

onMounted(() => {
  offsetWidth = carousel.value?.offsetWidth;
  len = props.assets.length;

  watch(
    () => currentIndex.value,
    (value) => (currentAsset.value = props.assets[value])
  );

  playSlide();
});

const playSlide = () => {
  if (len <= 1) return;

  translate.value = -currentIndex.value * offsetWidth;

  let currentItem = divDomList.value.get(currentIndex.value);

  nextTick(() => {
    slideRef.value.scrollLeft =
      currentItem.offsetLeft -
      slideRef.value.offsetWidth / 2 +
      currentItem.offsetWidth / 2;
  });
};

const previousSlide = () => {
  currentIndex.value = (currentIndex.value + len - 1) % len;
  playSlide();
};

const nextSlide = () => {
  currentIndex.value = (currentIndex.value + 1) % len;
  playSlide();
};

const jumpTo = (index) => {
  if (index === currentIndex.value) return;

  currentIndex.value = index;
  playSlide();
};

defineExpose({ currentIndex });
</script>

<style lang="less" scoped>
.carousel {
  position: relative;
  overflow: hidden;
  width: 100%;
  height: 100%;

  .carousel__wrapper {
    display: flex;
    transition: all 0.3s ease-out;
    width: 100%;
    height: 100%;

    .carousel__item {
      width: 100%;
      flex-shrink: 0;
    }

    img {
      width: 100%;
      max-height: 100%;
      object-fit: contain;
      position: absolute;
      top: 50%;
      transform: translateY(-50%);
    }
  }

  .carousel__arrow {
    .arrow_icon {
      width: 80px;
      height: 100%;

      position: absolute;
      top: 0;

      display: flex;
      justify-content: flex-end;
      align-items: center;

      background: radial-gradient(
        100% 53.22% at 100% 52.01%,
        rgba(0, 0, 0, 0.4) 0%,
        rgba(0, 0, 0, 0) 100%
      );

      opacity: 0.6;
      transition: opacity 0.15s;

      cursor: pointer;

      &:hover {
        opacity: 1;
      }

      img {
        margin-right: 24px;
      }
    }

    .prev_icon {
      left: 0;
      transform: rotate(180deg);
    }

    .next_icon {
      right: 0;
    }
  }

  .carousel__indicator {
    width: 100%;
    position: absolute;
    bottom: 16px;

    .carousel__indicator_thumbnails {
      max-width: 432px;
      
      position: absolute;
      bottom: 0;
      left: 50%;
      transform: translateX(-50%);

      display: flex;
      align-items: center;
      gap: 8px;

      overflow-x: auto;
      overflow-y: hidden;
      scroll-behavior: smooth;

      /* Firefox IE */
      scrollbar-width: none;
      -ms-overflow-style: none;

      &::-webkit-scrollbar {
        display: none;
      }

      .indicator {
        flex-shrink: 0;
        width: 64px;
        height: 64px;
        border: 2px solid #f2f2f200;
        border-radius: 8px;
        overflow: hidden;
        cursor: pointer;
      }

      .active {
        width: 72px;
        height: 72px;
        border: 2px solid #f2f2f2;
      }

      img {
        display: block;
        width: 100%;
        height: 100%;
        object-fit: cover;
      }
    }
  }
}
</style>

 使用

<template>
   <SwiperView :assets="lists" :index="index" />
</template>

<script setup>
    const lists = [{...}];
    const index = 0;
</script>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值