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>