hookehuyr

fix(轮播): 修复新闻轮播切换时的闪烁问题并实现无缝循环

添加过渡状态检测防止快速点击导致的闪烁
实现尾部克隆技术完成无缝循环效果
优化最后一张的显示逻辑和复位处理
......@@ -174,7 +174,7 @@
src="/src/assets/images/02 西园戒幢律寺三坛大戒法会/下@2x.png"
alt="下一张"
@click="nextSlide"
:class="{ disabled: currentSlide >= maxSlide }"
:class="{ disabled: isTransitioning }"
>
</div>
</div>
......@@ -342,6 +342,8 @@ const viewMore = (type) => {
// 新闻轮播相关逻辑
const newsCarousel = ref(null)
const currentSlide = ref(0)
const isTransitioning = ref(false)
const hasTailClone = ref(false)
// 新闻数据
const newsItems = ref([
......@@ -367,6 +369,9 @@ const newsItems = ref([
}
])
// 原始长度用于判断“最后一张”的逻辑
const initialLength = ref(newsItems.value.length)
// 计算最大滑动位置
const maxSlide = computed(() => {
return Math.max(0, newsItems.value.length - 1)
......@@ -376,14 +381,39 @@ const maxSlide = computed(() => {
const prevSlide = () => {
if (currentSlide.value > 0) {
currentSlide.value--
// 离开最后一张时移除尾部克隆,避免轨道长度异常
if (hasTailClone.value && currentSlide.value < initialLength.value - 1) {
newsItems.value.pop()
hasTailClone.value = false
}
updateCarouselPosition()
}
}
// 下一张
const nextSlide = () => {
if (currentSlide.value < maxSlide.value) {
if (isTransitioning.value) return
// 若还未到原始最后一张,正常前进
if (currentSlide.value < initialLength.value - 1) {
currentSlide.value++
// 当抵达原始最后一张时,预先追加尾部克隆以填充右侧空白
if (currentSlide.value === initialLength.value - 1 && !hasTailClone.value) {
newsItems.value.push(newsItems.value[0])
hasTailClone.value = true
}
isTransitioning.value = true
attachTransitionEndOnce(false)
updateCarouselPosition()
} else {
// 处于原始最后一张:使用尾部克隆进行无缝循环
if (!hasTailClone.value) {
newsItems.value.push(newsItems.value[0])
hasTailClone.value = true
}
currentSlide.value++
isTransitioning.value = true
attachTransitionEndOnce(true)
updateCarouselPosition()
}
}
......@@ -393,29 +423,44 @@ const updateCarouselPosition = () => {
const carouselEl = newsCarousel.value
if (!carouselEl) return
const containerEl = carouselEl.parentElement
const containerWidth = containerEl ? containerEl.clientWidth : 0
const firstItem = carouselEl.querySelector('.news-item')
if (!firstItem) return
const itemWidth = firstItem.getBoundingClientRect().width
// 读取gap像素值,回退到0.5rem(8px)
let gapStr = getComputedStyle(carouselEl).gap
if (!gapStr || gapStr === 'normal') {
gapStr = '8px'
}
const gapPx = parseFloat(gapStr) || 8
let distance = currentSlide.value * (itemWidth + gapPx)
const distance = currentSlide.value * (itemWidth + gapPx)
carouselEl.style.transform = `translateX(-${distance}px)`
}
// 最后一张时,保证完整显示(居中显示,无下一张时不被裁切)
if (currentSlide.value === maxSlide.value) {
const lastOffset = (newsItems.value.length - 1) * (itemWidth + gapPx)
const centerOffset = Math.max(0, (containerWidth - itemWidth) / 2)
distance = lastOffset - centerOffset
// 动画结束后复位(如处于无缝循环场景)
const attachTransitionEndOnce = (looping) => {
const el = newsCarousel.value
if (!el) {
isTransitioning.value = false
return
}
const handler = () => {
el.removeEventListener('transitionend', handler)
if (looping) {
const prevTransition = el.style.transition
el.style.transition = 'none'
// 移除临时克隆,重置到第一张
newsItems.value.pop()
hasTailClone.value = false
currentSlide.value = 0
updateCarouselPosition()
// 强制重绘后恢复过渡
void el.offsetHeight
el.style.transition = prevTransition || 'transform 0.3s ease'
}
isTransitioning.value = false
}
carouselEl.style.transform = `translateX(-${distance}px)`
el.addEventListener('transitionend', handler)
}
// 处理新闻点击事件
......