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

This commit is contained in:
hzj 2026-05-07 19:03:00 +08:00
parent 9ed7542df0
commit 09306f562a

View File

@ -259,11 +259,11 @@
<!-- 奖励 -->
<div class="reward-wrapper">
<div ref="rewardViewportRef" class="reward-scroll-viewport">
<div class="reward-scroll-viewport">
<!-- 滚动容器 -->
<div v-if="rewardList.length" ref="rewardScrollRef" class="scroll-wrapper">
<!-- 3套内容减少 iOS WebView 动画循环边界闪烁 -->
<div class="scroll-content" v-for="value in 3" :key="value">
<div v-if="rewardList.length" class="scroll-wrapper">
<!-- 套内容拉长循环周期减少 iOS WebView 动画循环边界闪烁 -->
<div class="scroll-content" v-for="value in scrollCopyCount" :key="value">
<div
v-for="(item, index) in rewardList"
:key="index"
@ -292,7 +292,7 @@
</template>
<script setup>
import { computed, nextTick, onUnmounted, ref, watch } from 'vue'
import { computed } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useLangStore } from '@/stores/lang'
import { useI18n } from 'vue-i18n'
@ -352,8 +352,7 @@ const props = defineProps({
},
})
const rewardViewportRef = ref(null)
const rewardScrollRef = ref(null)
const scrollCopyCount = 12
const rewardList = computed(() => {
return Array.isArray(props.rewards?.activityRewardProps) ? props.rewards.activityRewardProps : []
@ -382,109 +381,6 @@ const clickReward = (item) => {
topReward.value = item //
topRewardShow.value = true //
}
let rewardRestartTimer = null
let rewardAnimationFrame = null
let rewardLastFrameTime = 0
let rewardLoopDistance = 0
let rewardTranslatePosition = 0
const waitForLayoutFrame = () => {
return new Promise((resolve) => {
const requestFrame =
typeof window !== 'undefined' && window.requestAnimationFrame
? window.requestAnimationFrame
: (callback) => setTimeout(callback, 16)
requestFrame(() => {
requestFrame(resolve)
})
})
}
const restartRewardScroll = async () => {
if (rewardRestartTimer) {
clearTimeout(rewardRestartTimer)
}
if (rewardAnimationFrame) {
cancelAnimationFrame(rewardAnimationFrame)
rewardAnimationFrame = null
}
await nextTick()
rewardRestartTimer = setTimeout(async () => {
await waitForLayoutFrame()
const viewport = rewardViewportRef.value
const scrollWrapper = rewardScrollRef.value
if (!viewport || !scrollWrapper || !rewardList.value.length) {
rewardRestartTimer = null
return
}
const scrollCopies = scrollWrapper.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
}
rewardTranslatePosition = 0
viewport.scrollLeft = 0
scrollWrapper.style.transform = 'translate3d(0, 0, 0)'
rewardLastFrameTime = performance.now()
const step = (now) => {
const elapsed = now - rewardLastFrameTime
rewardLastFrameTime = now
const distancePerMs = rewardLoopDistance / 8000
rewardTranslatePosition += distancePerMs * elapsed
if (rewardTranslatePosition >= rewardLoopDistance) {
rewardTranslatePosition -= rewardLoopDistance
}
scrollWrapper.style.transform = `translate3d(${-rewardTranslatePosition}px, 0, 0)`
rewardAnimationFrame = requestAnimationFrame(step)
}
rewardAnimationFrame = requestAnimationFrame(step)
rewardRestartTimer = null
}, 80)
}
watch(
() =>
rewardList.value
.map((item) => `${item.type || ''}-${item.cover || ''}-${item.content || ''}-${item.quantity || ''}`)
.join('|'),
() => {
restartRewardScroll()
},
{ flush: 'post', immediate: true },
)
onUnmounted(() => {
if (rewardRestartTimer) {
clearTimeout(rewardRestartTimer)
rewardRestartTimer = null
}
if (rewardAnimationFrame) {
cancelAnimationFrame(rewardAnimationFrame)
rewardAnimationFrame = null
}
if (rewardScrollRef.value) {
rewardScrollRef.value.style.transform = ''
}
})
</script>
<style lang="scss" scoped>
@ -540,6 +436,28 @@ onUnmounted(() => {
will-change: transform;
}
@keyframes reward-scroll-left {
0% {
transform: translate3d(0, 0, 0);
-webkit-transform: translate3d(0, 0, 0);
}
100% {
transform: translate3d(-91.666667%, 0, 0);
-webkit-transform: translate3d(-91.666667%, 0, 0);
}
}
@keyframes reward-scroll-right {
0% {
transform: translate3d(0, 0, 0);
-webkit-transform: translate3d(0, 0, 0);
}
100% {
transform: translate3d(91.666667%, 0, 0);
-webkit-transform: translate3d(91.666667%, 0, 0);
}
}
.scroll-content {
flex: 0 0 auto;
display: flex;
@ -576,6 +494,14 @@ onUnmounted(() => {
margin-right: 4vw;
}
[dir='ltr'] .scroll-wrapper {
animation: reward-scroll-left 88s linear infinite;
}
[dir='rtl'] .scroll-wrapper {
animation: reward-scroll-left 88s linear infinite;
}
@media screen and (max-width: 360px) {
* {
font-size: 10px;