```html
<template> <!-- @touchend="handleTouchEnd" --> <!-- @touchstart="handleTouchStart" --> <div class="carousel-conainer"> <div class="carousel-list" :style="{ transform: `translateX(-${currentIndex * 100}%)` }" > <div v-for="(item) in items" :key="item.id" class="carousel-item"> <img :src="item.image" :alt="item.title" /> </div> </div> <div class="carousel-control prev" @click="prevSlide"><</div> <div class="carousel-control next" @click="nextSlide">></div> <div class="indicator"> <span v-for="(childItem,childIndex) in items" :key="childItem.id" :class="{ active: currentIndex === childIndex }" @click="currentIndex = childIndex" ></span> </div> </div> </template> ``` ```js <script setup> import { ref, onMounted, onUnmounted, watch } from 'vue'const props = defineProps({ items: { type: Array, required: true, validator: items => Array.isArray(items) && items.length > 0, // 增加验证,确保items为数组且不为空 }, autoplayInterval: { type: Number, default: 3000, }, })
const currentIndex = ref(0) // const touchStartX = ref(0) const isVisible = ref(true) // let autoplayInterval = null
// const updateAutoplay = () => { // if (isVisible.value && props.autoplayInterval) { // autoplayInterval = setInterval(nextSlide, props.autoplayInterval) // } else { // clearInterval(autoplayInterval) // } // } // const doms = ref({ // carousleList: document.querySelector('.carousel-list'), // indicator: document.querySelector('.indicator'), // prevBtn: document.querySelector('.prev'), // nextBtn: document.querySelector('.next'), // }) const doms = ref({ carousleList: null, indicator: null, }) const count = ref(props.items.length) const init = () => { doms.value.carousleList = document.querySelector('.carousel-list') doms.value.indicator = document.querySelector('.indicator') // 复制第一张图片到最后,复制最后一张图片到第一 if (doms.value.carousleList) { const firstCloned = doms.value.carousleList.firstElementChild.cloneNode(true) const lastCloned = doms.value.carousleList.lastElementChild.cloneNode(true) doms.value.carousleList.appendChild(firstCloned) doms.value.carousleList.insertBefore( lastCloned, doms.value.carousleList.firstElementChild, ) lastCloned.style.marginLeft = '-100%' } else { console.error("Carousel list not found"); } // 绑定事件 }
const moveTo = index => { doms.value.carousleList.style.transform = `translateX(-${index * 100}%)` doms.value.carousleList.style.transition = 'transform 0.5s ease' doms.value.indicator.querySelectorAll('span')[index].classList.add('active') doms.value.indicator .querySelectorAll('span') [currentIndex.value].classList.remove('active') currentIndex.value = index } const nextSlide = () => { if (currentIndex.value === count.value - 1) { doms.value.carousleList.style.transform = `translateX(100%)` doms.value.carousleList.style.transition = 'none' // 让浏览器渲染 doms.value.carousleList.offsetHeight moveTo(0) } else { moveTo((currentIndex.value + 1) % props.items.length) } } const prevSlide = () => { // if (props.items.length === 0) return // 防止没有项目时出错 if (currentIndex.value === 0) { console.log('currentIndex.value === 0') doms.value.carousleList.style.transform = `translateX(-${count.value * 100}%)` doms.value.carousleList.style.transition = 'none' // 让浏览器渲染 doms.value.carousleList.offsetHeight moveTo(count.value - 1) }else{ moveTo((currentIndex.value - 1 + props.items.length) % props.items.length) } }
// const nextSlide = () => { // if (props.items.length === 0) return // 防止没有项目时出错 // currentIndex.value = (currentIndex.value + 1) % props.items.length // }
// const prevSlide = () => { // if (props.items.length === 0) return // 防止没有项目时出错 // currentIndex.value = // (currentIndex.value - 1 + props.items.length) % props.items.length // }
const handleVisibilityChange = () => { isVisible.value = !document.hidden // updateAutoplay() }
// const handleTouchStart = e => { // if (e.touches.length > 0) { // touchStartX.value = e.touches[0].clientX // } // }
// const handleTouchEnd = e => { // if (e.changedTouches.length > 0) { // const touchEndX = e.changedTouches[0].clientX // const diff = touchStartX.value - touchEndX // if (Math.abs(diff) > 50) { // diff > 0 ? nextSlide() : prevSlide() // } // } // }
onMounted(() => { init() // updateAutoplay() document.addEventListener('visibilitychange', handleVisibilityChange) })
onUnmounted(() => { // clearInterval(autoplayInterval) document.removeEventListener('visibilitychange', handleVisibilityChange) })
// watch(isVisible, updateAutoplay) </script> ``` ```css
<style scoped lang="scss"> .carousel-conainer { position: relative; width: 500px; // height: 400px; margin: 50px auto; outline: 1px solid #000; overflow: hidden; .carousel-list { display: flex; transition: transform 0.5s ease; height: 100%;
.carousel-item { flex: 0 0 100%; height: 100%; display: flex; flex-direction: column; justify-content: center; align-items: center; }
.carousel-item img { max-width: 100%; max-height: 80%; object-fit: contain; } } .carousel-control { position: absolute; top: 50%; width: 40px; height: 40px; transform: translateY(-50%); background: rgba(0, 0, 0, 0.4); border-radius: 50%; color: white; border: none; padding: 10px; cursor: pointer; display: flex; justify-content: center; align-items: center; &:hover { background: rgba(0, 0, 0, 1); } }
.carousel-control.prev { left: 10px; }
.carousel-control.next { right: 10px; } .indicator { position: absolute; bottom: 10px; left: 50%; transform: translateX(-50%); display: flex; justify-content: center; align-items: center; width: 100%; height: 20px; // background: rgba(0, 0, 0, 0.5); color: white; font-size: 12px; text-align: center; span { margin: 0 5px; width: 10px; height: 10px; border-radius: 50%; background: #fff; border: 1px solid #000; cursor: pointer; &.active { background: red; } } } } </style>
```
标签:const,轮播,items,value,props,doms,carousleList,无缝 From: https://www.cnblogs.com/xianlling/p/18478112