diff --git a/src/views/Ranking/Couple/components/topUser.vue b/src/views/Ranking/Couple/components/topUser.vue index a18a501..cd2b8e6 100644 --- a/src/views/Ranking/Couple/components/topUser.vue +++ b/src/views/Ranking/Couple/components/topUser.vue @@ -259,15 +259,9 @@
-
+
-
+
{ return Array.isArray(props.rewards?.activityRewardProps) ? props.rewards.activityRewardProps : [] @@ -388,6 +382,9 @@ const clickReward = (item) => { topRewardShow.value = true // 显示奖品 } let rewardRestartTimer = null +let rewardAnimationFrame = null +let rewardLastFrameTime = 0 +let rewardLoopDistance = 0 const waitForLayoutFrame = () => { return new Promise((resolve) => { @@ -406,13 +403,52 @@ const restartRewardScroll = async () => { if (rewardRestartTimer) { clearTimeout(rewardRestartTimer) } + if (rewardAnimationFrame) { + cancelAnimationFrame(rewardAnimationFrame) + rewardAnimationFrame = null + } - scrollReady.value = false await nextTick() rewardRestartTimer = setTimeout(async () => { await waitForLayoutFrame() - scrollReady.value = rewardList.value.length > 0 + const viewport = rewardViewportRef.value + if (!viewport || !rewardList.value.length) { + rewardRestartTimer = null + return + } + + const scrollCopies = viewport.querySelectorAll('.scroll-content') + if (scrollCopies.length < 2) { + rewardRestartTimer = null + return + } + + rewardLoopDistance = + Math.abs(scrollCopies[1].offsetLeft - scrollCopies[0].offsetLeft) || + scrollCopies[0].scrollWidth + + if (!rewardLoopDistance) { + rewardRestartTimer = null + return + } + + viewport.scrollLeft = 0 + rewardLastFrameTime = performance.now() + + const step = (now) => { + const elapsed = now - rewardLastFrameTime + rewardLastFrameTime = now + const distancePerMs = rewardLoopDistance / 8000 + const nextScrollLeft = viewport.scrollLeft + distancePerMs * elapsed + + viewport.scrollLeft = + nextScrollLeft >= rewardLoopDistance ? nextScrollLeft - rewardLoopDistance : nextScrollLeft + + rewardAnimationFrame = requestAnimationFrame(step) + } + + rewardAnimationFrame = requestAnimationFrame(step) rewardRestartTimer = null }, 80) } @@ -433,6 +469,10 @@ onUnmounted(() => { clearTimeout(rewardRestartTimer) rewardRestartTimer = null } + if (rewardAnimationFrame) { + cancelAnimationFrame(rewardAnimationFrame) + rewardAnimationFrame = null + } }) @@ -470,13 +510,17 @@ onUnmounted(() => { right: 15vw; } +.reward-scroll-viewport { + height: 100%; + display: flex; + align-items: center; + overflow: hidden; + direction: ltr; +} + .scroll-wrapper { display: flex; width: fit-content; - transform: translate3d(0, 0, 0); - -webkit-transform: translate3d(0, 0, 0); - backface-visibility: hidden; - -webkit-backface-visibility: hidden; } .scroll-content { @@ -485,47 +529,15 @@ onUnmounted(() => { align-items: center; } -@keyframes scroll-left { - 0% { - transform: translate3d(0, 0, 0); - -webkit-transform: translate3d(0, 0, 0); - } - 100% { - transform: translate3d(-33.333333%, 0, 0); - -webkit-transform: translate3d(-33.333333%, 0, 0); - } -} - -@keyframes scroll-right { - 0% { - transform: translate3d(0, 0, 0); - -webkit-transform: translate3d(0, 0, 0); - } - 100% { - transform: translate3d(33.333333%, 0, 0); - -webkit-transform: translate3d(33.333333%, 0, 0); - } -} - [dir='ltr'] .scroll-content { margin-left: 4vw; } [dir='rtl'] .scroll-content { - margin-left: auto; + direction: rtl; margin-right: 4vw; } -[dir='ltr'] .scroll-wrapper-active { - animation: scroll-left 8s linear infinite; - will-change: transform; -} - -[dir='rtl'] .scroll-wrapper-active { - animation: scroll-right 8s linear infinite; - will-change: transform; -} - @media screen and (max-width: 360px) { * { font-size: 10px;