hookehuyr

refactor(轮播组件): 重构新闻轮播逻辑实现无缝滚动

移除克隆项动态增减逻辑,改用固定克隆首尾项的方式
简化轮播位置更新和动画控制逻辑
...@@ -118,7 +118,7 @@ ...@@ -118,7 +118,7 @@
118 <!-- 轮播容器 --> 118 <!-- 轮播容器 -->
119 <div class="news-carousel-container"> 119 <div class="news-carousel-container">
120 <div class="news-carousel" ref="newsCarousel"> 120 <div class="news-carousel" ref="newsCarousel">
121 - <div class="news-item" v-for="(item, index) in newsItems" :key="index" @click="handleNewsClick(item)"> 121 + <div class="news-item" v-for="(item, index) in displayNewsItems" :key="index" @click="handleNewsClick(item)">
122 <div class="news-image"> 122 <div class="news-image">
123 <img :src="item.image" :alt="item.title"> 123 <img :src="item.image" :alt="item.title">
124 </div> 124 </div>
...@@ -136,14 +136,12 @@ ...@@ -136,14 +136,12 @@
136 src="https://cdn.ipadbiz.cn/stdj/images/home/%E4%B8%8A@2x.png" 136 src="https://cdn.ipadbiz.cn/stdj/images/home/%E4%B8%8A@2x.png"
137 alt="上一张" 137 alt="上一张"
138 @click="prevSlide" 138 @click="prevSlide"
139 - :class="{ disabled: currentSlide === 0 }"
140 > 139 >
141 <img 140 <img
142 class="nav-btn next-btn" 141 class="nav-btn next-btn"
143 src="https://cdn.ipadbiz.cn/stdj/images/home/%E4%B8%8B@2x.png" 142 src="https://cdn.ipadbiz.cn/stdj/images/home/%E4%B8%8B@2x.png"
144 alt="下一张" 143 alt="下一张"
145 @click="nextSlide" 144 @click="nextSlide"
146 - :class="{ disabled: isTransitioning }"
147 > 145 >
148 </div> 146 </div>
149 </div> 147 </div>
...@@ -276,6 +274,11 @@ onMounted(async () => { ...@@ -276,6 +274,11 @@ onMounted(async () => {
276 image: item.photo || '', 274 image: item.photo || '',
277 post_link: item.post_link || '', 275 post_link: item.post_link || '',
278 })) || []; 276 })) || [];
277 +
278 + // 初始化轮播位置
279 + nextTick(() => {
280 + updateCarouselPosition(false)
281 + })
279 } 282 }
280 }) 283 })
281 284
...@@ -317,59 +320,61 @@ const viewMore = (type, item) => { ...@@ -317,59 +320,61 @@ const viewMore = (type, item) => {
317 320
318 // 新闻轮播相关逻辑 321 // 新闻轮播相关逻辑
319 const newsCarousel = ref(null) 322 const newsCarousel = ref(null)
320 -const currentSlide = ref(0) 323 +const currentSlide = ref(1) // 从1开始,因为0是克隆的最后一项
321 const isTransitioning = ref(false) 324 const isTransitioning = ref(false)
322 -const hasTailClone = ref(false)
323 325
324 // 新闻数据 326 // 新闻数据
325 const newsItems = ref([]) 327 const newsItems = ref([])
326 - 328 +const displayNewsItems = computed(() => {
327 -// 原始长度用于判断“最后一张”的逻辑 329 + if (newsItems.value.length === 0) {
328 -const initialLength = ref(newsItems.value.length) 330 + return []
331 + }
332 + // 克隆第一项和最后一项以实现无缝滚动
333 + return [newsItems.value[newsItems.value.length - 1], ...newsItems.value, newsItems.value[0]]
334 +})
329 335
330 // 上一张 336 // 上一张
331 const prevSlide = () => { 337 const prevSlide = () => {
332 - if (currentSlide.value > 0) { 338 + if (isTransitioning.value) return
333 - currentSlide.value-- 339 + isTransitioning.value = true
334 - // 离开最后一张时移除尾部克隆,避免轨道长度异常 340 + currentSlide.value--
335 - if (hasTailClone.value && currentSlide.value < initialLength.value - 1) { 341 + updateCarouselPosition()
336 - newsItems.value.pop() 342 +
337 - hasTailClone.value = false 343 + if (currentSlide.value === 0) {
338 - } 344 + setTimeout(() => {
339 - updateCarouselPosition() 345 + isTransitioning.value = false
346 + currentSlide.value = newsItems.value.length
347 + updateCarouselPosition(false) // 无动画地跳转
348 + }, 300) // 等待过渡动画完成
349 + } else {
350 + setTimeout(() => {
351 + isTransitioning.value = false
352 + }, 300)
340 } 353 }
341 } 354 }
342 355
343 // 下一张 356 // 下一张
344 const nextSlide = () => { 357 const nextSlide = () => {
345 if (isTransitioning.value) return 358 if (isTransitioning.value) return
346 - 359 + isTransitioning.value = true
347 - // 若还未到原始最后一张,正常前进 360 + currentSlide.value++
348 - if (currentSlide.value < initialLength.value - 1) { 361 + updateCarouselPosition()
349 - currentSlide.value++ 362 +
350 - // 当抵达原始最后一张时,预先追加尾部克隆以填充右侧空白 363 + if (currentSlide.value === displayNewsItems.value.length - 1) {
351 - if (currentSlide.value === initialLength.value - 1 && !hasTailClone.value) { 364 + setTimeout(() => {
352 - newsItems.value.push(newsItems.value[0]) 365 + isTransitioning.value = false
353 - hasTailClone.value = true 366 + currentSlide.value = 1
354 - } 367 + updateCarouselPosition(false) // 无动画地跳转
355 - isTransitioning.value = true 368 + }, 300) // 等待过渡动画完成
356 - attachTransitionEndOnce(false)
357 - updateCarouselPosition()
358 } else { 369 } else {
359 - // 处于原始最后一张:使用尾部克隆进行无缝循环 370 + setTimeout(() => {
360 - if (!hasTailClone.value) { 371 + isTransitioning.value = false
361 - newsItems.value.push(newsItems.value[0]) 372 + }, 300)
362 - hasTailClone.value = true
363 - }
364 - currentSlide.value++
365 - isTransitioning.value = true
366 - attachTransitionEndOnce(true)
367 - updateCarouselPosition()
368 } 373 }
369 } 374 }
370 375
371 -// 更新轮播位置:按卡片宽度+间距进行像素级位移 376 +// 更新轮播位置
372 -const updateCarouselPosition = () => { 377 +const updateCarouselPosition = (animated = true) => {
373 const carouselEl = newsCarousel.value 378 const carouselEl = newsCarousel.value
374 if (!carouselEl) return 379 if (!carouselEl) return
375 380
...@@ -384,35 +389,10 @@ const updateCarouselPosition = () => { ...@@ -384,35 +389,10 @@ const updateCarouselPosition = () => {
384 const gapPx = parseFloat(gapStr) || 8 389 const gapPx = parseFloat(gapStr) || 8
385 390
386 const distance = currentSlide.value * (itemWidth + gapPx) 391 const distance = currentSlide.value * (itemWidth + gapPx)
392 + carouselEl.style.transition = animated ? 'transform 0.3s ease' : 'none'
387 carouselEl.style.transform = `translateX(-${distance}px)` 393 carouselEl.style.transform = `translateX(-${distance}px)`
388 } 394 }
389 395
390 -// 动画结束后复位(如处于无缝循环场景)
391 -const attachTransitionEndOnce = (looping) => {
392 - const el = newsCarousel.value
393 - if (!el) {
394 - isTransitioning.value = false
395 - return
396 - }
397 - const handler = () => {
398 - el.removeEventListener('transitionend', handler)
399 - if (looping) {
400 - const prevTransition = el.style.transition
401 - el.style.transition = 'none'
402 - // 移除临时克隆,重置到第一张
403 - newsItems.value.pop()
404 - hasTailClone.value = false
405 - currentSlide.value = 0
406 - updateCarouselPosition()
407 - // 强制重绘后恢复过渡
408 - void el.offsetHeight
409 - el.style.transition = prevTransition || 'transform 0.3s ease'
410 - }
411 - isTransitioning.value = false
412 - }
413 - el.addEventListener('transitionend', handler)
414 -}
415 -
416 // 处理新闻点击事件 396 // 处理新闻点击事件
417 const handleNewsClick = (item) => { 397 const handleNewsClick = (item) => {
418 // 这里可以添加跳转到新闻详情页面的逻辑 398 // 这里可以添加跳转到新闻详情页面的逻辑
......