hookehuyr

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

添加过渡状态检测防止快速点击导致的闪烁
实现尾部克隆技术完成无缝循环效果
优化最后一张的显示逻辑和复位处理
...@@ -174,7 +174,7 @@ ...@@ -174,7 +174,7 @@
174 src="/src/assets/images/02 西园戒幢律寺三坛大戒法会/下@2x.png" 174 src="/src/assets/images/02 西园戒幢律寺三坛大戒法会/下@2x.png"
175 alt="下一张" 175 alt="下一张"
176 @click="nextSlide" 176 @click="nextSlide"
177 - :class="{ disabled: currentSlide >= maxSlide }" 177 + :class="{ disabled: isTransitioning }"
178 > 178 >
179 </div> 179 </div>
180 </div> 180 </div>
...@@ -342,6 +342,8 @@ const viewMore = (type) => { ...@@ -342,6 +342,8 @@ const viewMore = (type) => {
342 // 新闻轮播相关逻辑 342 // 新闻轮播相关逻辑
343 const newsCarousel = ref(null) 343 const newsCarousel = ref(null)
344 const currentSlide = ref(0) 344 const currentSlide = ref(0)
345 +const isTransitioning = ref(false)
346 +const hasTailClone = ref(false)
345 347
346 // 新闻数据 348 // 新闻数据
347 const newsItems = ref([ 349 const newsItems = ref([
...@@ -367,6 +369,9 @@ const newsItems = ref([ ...@@ -367,6 +369,9 @@ const newsItems = ref([
367 } 369 }
368 ]) 370 ])
369 371
372 +// 原始长度用于判断“最后一张”的逻辑
373 +const initialLength = ref(newsItems.value.length)
374 +
370 // 计算最大滑动位置 375 // 计算最大滑动位置
371 const maxSlide = computed(() => { 376 const maxSlide = computed(() => {
372 return Math.max(0, newsItems.value.length - 1) 377 return Math.max(0, newsItems.value.length - 1)
...@@ -376,14 +381,39 @@ const maxSlide = computed(() => { ...@@ -376,14 +381,39 @@ const maxSlide = computed(() => {
376 const prevSlide = () => { 381 const prevSlide = () => {
377 if (currentSlide.value > 0) { 382 if (currentSlide.value > 0) {
378 currentSlide.value-- 383 currentSlide.value--
384 + // 离开最后一张时移除尾部克隆,避免轨道长度异常
385 + if (hasTailClone.value && currentSlide.value < initialLength.value - 1) {
386 + newsItems.value.pop()
387 + hasTailClone.value = false
388 + }
379 updateCarouselPosition() 389 updateCarouselPosition()
380 } 390 }
381 } 391 }
382 392
383 // 下一张 393 // 下一张
384 const nextSlide = () => { 394 const nextSlide = () => {
385 - if (currentSlide.value < maxSlide.value) { 395 + if (isTransitioning.value) return
396 +
397 + // 若还未到原始最后一张,正常前进
398 + if (currentSlide.value < initialLength.value - 1) {
399 + currentSlide.value++
400 + // 当抵达原始最后一张时,预先追加尾部克隆以填充右侧空白
401 + if (currentSlide.value === initialLength.value - 1 && !hasTailClone.value) {
402 + newsItems.value.push(newsItems.value[0])
403 + hasTailClone.value = true
404 + }
405 + isTransitioning.value = true
406 + attachTransitionEndOnce(false)
407 + updateCarouselPosition()
408 + } else {
409 + // 处于原始最后一张:使用尾部克隆进行无缝循环
410 + if (!hasTailClone.value) {
411 + newsItems.value.push(newsItems.value[0])
412 + hasTailClone.value = true
413 + }
386 currentSlide.value++ 414 currentSlide.value++
415 + isTransitioning.value = true
416 + attachTransitionEndOnce(true)
387 updateCarouselPosition() 417 updateCarouselPosition()
388 } 418 }
389 } 419 }
...@@ -393,29 +423,44 @@ const updateCarouselPosition = () => { ...@@ -393,29 +423,44 @@ const updateCarouselPosition = () => {
393 const carouselEl = newsCarousel.value 423 const carouselEl = newsCarousel.value
394 if (!carouselEl) return 424 if (!carouselEl) return
395 425
396 - const containerEl = carouselEl.parentElement
397 - const containerWidth = containerEl ? containerEl.clientWidth : 0
398 const firstItem = carouselEl.querySelector('.news-item') 426 const firstItem = carouselEl.querySelector('.news-item')
399 if (!firstItem) return 427 if (!firstItem) return
400 428
401 const itemWidth = firstItem.getBoundingClientRect().width 429 const itemWidth = firstItem.getBoundingClientRect().width
402 - // 读取gap像素值,回退到0.5rem(8px)
403 let gapStr = getComputedStyle(carouselEl).gap 430 let gapStr = getComputedStyle(carouselEl).gap
404 if (!gapStr || gapStr === 'normal') { 431 if (!gapStr || gapStr === 'normal') {
405 gapStr = '8px' 432 gapStr = '8px'
406 } 433 }
407 const gapPx = parseFloat(gapStr) || 8 434 const gapPx = parseFloat(gapStr) || 8
408 435
409 - let distance = currentSlide.value * (itemWidth + gapPx) 436 + const distance = currentSlide.value * (itemWidth + gapPx)
437 + carouselEl.style.transform = `translateX(-${distance}px)`
438 +}
410 439
411 - // 最后一张时,保证完整显示(居中显示,无下一张时不被裁切) 440 +// 动画结束后复位(如处于无缝循环场景)
412 - if (currentSlide.value === maxSlide.value) { 441 +const attachTransitionEndOnce = (looping) => {
413 - const lastOffset = (newsItems.value.length - 1) * (itemWidth + gapPx) 442 + const el = newsCarousel.value
414 - const centerOffset = Math.max(0, (containerWidth - itemWidth) / 2) 443 + if (!el) {
415 - distance = lastOffset - centerOffset 444 + isTransitioning.value = false
445 + return
446 + }
447 + const handler = () => {
448 + el.removeEventListener('transitionend', handler)
449 + if (looping) {
450 + const prevTransition = el.style.transition
451 + el.style.transition = 'none'
452 + // 移除临时克隆,重置到第一张
453 + newsItems.value.pop()
454 + hasTailClone.value = false
455 + currentSlide.value = 0
456 + updateCarouselPosition()
457 + // 强制重绘后恢复过渡
458 + void el.offsetHeight
459 + el.style.transition = prevTransition || 'transform 0.3s ease'
416 } 460 }
417 - 461 + isTransitioning.value = false
418 - carouselEl.style.transform = `translateX(-${distance}px)` 462 + }
463 + el.addEventListener('transitionend', handler)
419 } 464 }
420 465
421 // 处理新闻点击事件 466 // 处理新闻点击事件
......