fix(cp活动): 修复苹果端奖励滚动动画闪烁问题

This commit is contained in:
hzj 2026-05-07 18:36:38 +08:00
parent 07afff8cf8
commit 55d49d026f

View File

@ -259,15 +259,9 @@
<!-- 奖励 -->
<div class="reward-wrapper">
<div
style="height: 100%; display: flex; align-items: center; overflow: hidden"
>
<div ref="rewardViewportRef" class="reward-scroll-viewport">
<!-- 滚动容器 -->
<div
v-if="rewardList.length"
class="scroll-wrapper"
:class="{ 'scroll-wrapper-active': scrollReady }"
>
<div v-if="rewardList.length" class="scroll-wrapper">
<!-- 3套内容减少 iOS WebView 动画循环边界闪烁 -->
<div class="scroll-content" v-for="value in 3" :key="value">
<div
@ -358,7 +352,7 @@ const props = defineProps({
},
})
const scrollReady = ref(false)
const rewardViewportRef = ref(null)
const rewardList = computed(() => {
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
}
})
</script>
@ -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;