diff --git a/apps/viewer/src/components/ads/AdCarousel.vue b/apps/viewer/src/components/ads/AdCarousel.vue index d1cb670..22746db 100644 --- a/apps/viewer/src/components/ads/AdCarousel.vue +++ b/apps/viewer/src/components/ads/AdCarousel.vue @@ -34,6 +34,8 @@ const SLIDE_MS = 450; const trackPos = ref(1); /** 瞬时回位期间关闭过渡(transform 跳转不产生动画)。 */ const noTransition = ref(false); +/** 轨道元素(snapBack 强制重排用)。 */ +const trackEl = ref(null); let snapTimer: ReturnType | undefined; /** 轨道帧序列:{ ad, real }——real 为真实 index,克隆帧为 null。 */ @@ -57,9 +59,16 @@ function clearSnapTimer(): void { async function snapBack(): Promise { noTransition.value = true; trackPos.value = current.value + 1; - // 同帧应用「关过渡 + 新 transform」,下一拍恢复过渡(transform 已就位不触发)。 await nextTick(); - noTransition.value = false; + // 强制重排 + 双 rAF:确保「无过渡 + 新 transform」先完成一次绘制,再恢复 + // 过渡。若在同一帧内直接恢复(浏览器样式计算时过渡仍在),回跳会变成可见 + // 的反向长滚(2026-09-29 线上反馈根因;测试假时钟下恰好掩盖)。 + void trackEl.value?.offsetWidth; + requestAnimationFrame(() => { + requestAnimationFrame(() => { + noTransition.value = false; + }); + }); } /** 移动到真实 index(圆点/滑动/自动轮播共用):环回走克隆位 + 到位后回跳。 */ @@ -170,6 +179,7 @@ function openAd(ad: PublicAd): void { 令环回也是单帧宽自然滑入(到位后关过渡瞬时回位,见 script);仅活跃 真实帧可聚焦;错误上报只看活跃帧。 -->