<script setup> import { onMounted, onUnmounted, ref, useSlots } from 'vue' const slots = useSlots() const slides = ref([]) const originalSlidesLength = ref(0) const currentIndex = ref(0) const isDragging = ref(false) const startX = ref(0) const currentTranslate = ref(0) const prevTranslate = ref(0) const props = defineProps({ autoPlay: { type: Boolean, default: false } }) onMounted(() => { const originalSlides = slots.default()[0].children.map((slide, index) => ({ content: slide, key: index })) originalSlidesLength.value = originalSlides.length slides.value = [...originalSlides, originalSlides[0]] startAutoPlay() }) let autoPlayInterval const startAutoPlay = () => { if (!props.autoPlay) return autoPlayInterval = setInterval(() => { goToNextSlide() }, 3000) } const goToNextSlide = () => { if (currentIndex.value < slides.value.length - 2) { currentIndex.value += 1 } else { currentIndex.value = 0 updateTranslate(true) return } updateTranslate() } const startDrag = (event) => { isDragging.value = true startX.value = event.clientX prevTranslate.value = currentTranslate.value clearInterval(autoPlayInterval) } const onDrag = (event) => { if (isDragging.value) { const currentX = event.clientX const diff = currentX - startX.value currentTranslate.value = prevTranslate.value + (diff / window.innerWidth) * 100 } } const endDrag = () => { if (isDragging.value) { isDragging.value = false const movedBy = currentTranslate.value - prevTranslate.value if (movedBy < -10 && currentIndex.value < slides.value.length - 2) { currentIndex.value += 1 } else if (movedBy > 10 && currentIndex.value > 0) { currentIndex.value -= 1 } else if (movedBy > 10 && currentIndex.value === 0) { currentIndex.value = slides.value.length - 2 } else if (movedBy < -10 && currentIndex.value === slides.value.length - 2) { currentIndex.value = 0 } updateTranslate() startAutoPlay() } } const updateTranslate = (instant = false) => { prevTranslate.value = -currentIndex.value * 100 currentTranslate.value = prevTranslate.value if (instant) { requestAnimationFrame(() => { currentTranslate.value = prevTranslate.value }) } } const goToSlide = (index) => { currentIndex.value = index updateTranslate() } onUnmounted(() => { clearInterval(autoPlayInterval) }) </script> <template> <div class="carousel-container"> <div class="carousel" @mousedown="startDrag" @mousemove="onDrag" @mouseup="endDrag" @mouseleave="endDrag" :style="{ transform: `translateX(${currentTranslate}%)`, transition: isDragging ? 'none' : 'transform 0.3s ease' }" > <div v-for="(slide, index) in slides" :key="slide.key" class="slide"> <component :is="slide.content"></component> </div> </div> <div class="indicators"> <span v-for="(indicator, index) in slides.slice(0, originalSlidesLength)" :key="index" class="indicator" :class="{ active: currentIndex === index || (currentIndex === slides.length - 1 && index === 0) }" @click="goToSlide(index)" ></span> </div> </div> </template> <style scoped> .carousel-container { position: relative; width: 100%; height: 100%; margin: 0 auto; overflow: hidden; padding-bottom: 30px; } .carousel { height: 100%; display: flex; } .slide { min-width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; font-size: 2em; color: white; user-select: none; } .indicators { position: absolute; bottom: 10px; left: 50%; transform: translateX(-50%); display: flex; gap: 10px; } .indicator { width: 10px; height: 6px; background-color: rgba(255, 255, 255, 0.5); cursor: pointer; transition: background-color 0.3s ease; } .indicator.active { background-color: rgba(255, 255, 255, 1); } </style>