fix(轮播): 修复新闻轮播切换时的闪烁问题并实现无缝循环
添加过渡状态检测防止快速点击导致的闪烁 实现尾部克隆技术完成无缝循环效果 优化最后一张的显示逻辑和复位处理
Showing
1 changed file
with
58 additions
and
13 deletions
| ... | @@ -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) { | ||
| 386 | currentSlide.value++ | 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 | + } | ||
| 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' | ||
| 460 | + } | ||
| 461 | + isTransitioning.value = false | ||
| 416 | } | 462 | } |
| 417 | - | 463 | + el.addEventListener('transitionend', handler) |
| 418 | - carouselEl.style.transform = `translateX(-${distance}px)` | ||
| 419 | } | 464 | } |
| 420 | 465 | ||
| 421 | // 处理新闻点击事件 | 466 | // 处理新闻点击事件 | ... | ... |
-
Please register or login to post a comment