fix(cp活动): 修复苹果端奖励滚动动画闪烁问题
This commit is contained in:
parent
07afff8cf8
commit
55d49d026f
@ -259,15 +259,9 @@
|
|||||||
|
|
||||||
<!-- 奖励 -->
|
<!-- 奖励 -->
|
||||||
<div class="reward-wrapper">
|
<div class="reward-wrapper">
|
||||||
<div
|
<div ref="rewardViewportRef" class="reward-scroll-viewport">
|
||||||
style="height: 100%; display: flex; align-items: center; overflow: hidden"
|
|
||||||
>
|
|
||||||
<!-- 滚动容器 -->
|
<!-- 滚动容器 -->
|
||||||
<div
|
<div v-if="rewardList.length" class="scroll-wrapper">
|
||||||
v-if="rewardList.length"
|
|
||||||
class="scroll-wrapper"
|
|
||||||
:class="{ 'scroll-wrapper-active': scrollReady }"
|
|
||||||
>
|
|
||||||
<!-- 3套内容,减少 iOS WebView 动画循环边界闪烁 -->
|
<!-- 3套内容,减少 iOS WebView 动画循环边界闪烁 -->
|
||||||
<div class="scroll-content" v-for="value in 3" :key="value">
|
<div class="scroll-content" v-for="value in 3" :key="value">
|
||||||
<div
|
<div
|
||||||
@ -358,7 +352,7 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const scrollReady = ref(false)
|
const rewardViewportRef = ref(null)
|
||||||
|
|
||||||
const rewardList = computed(() => {
|
const rewardList = computed(() => {
|
||||||
return Array.isArray(props.rewards?.activityRewardProps) ? props.rewards.activityRewardProps : []
|
return Array.isArray(props.rewards?.activityRewardProps) ? props.rewards.activityRewardProps : []
|
||||||
@ -388,6 +382,9 @@ const clickReward = (item) => {
|
|||||||
topRewardShow.value = true // 显示奖品
|
topRewardShow.value = true // 显示奖品
|
||||||
}
|
}
|
||||||
let rewardRestartTimer = null
|
let rewardRestartTimer = null
|
||||||
|
let rewardAnimationFrame = null
|
||||||
|
let rewardLastFrameTime = 0
|
||||||
|
let rewardLoopDistance = 0
|
||||||
|
|
||||||
const waitForLayoutFrame = () => {
|
const waitForLayoutFrame = () => {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
@ -406,13 +403,52 @@ const restartRewardScroll = async () => {
|
|||||||
if (rewardRestartTimer) {
|
if (rewardRestartTimer) {
|
||||||
clearTimeout(rewardRestartTimer)
|
clearTimeout(rewardRestartTimer)
|
||||||
}
|
}
|
||||||
|
if (rewardAnimationFrame) {
|
||||||
|
cancelAnimationFrame(rewardAnimationFrame)
|
||||||
|
rewardAnimationFrame = null
|
||||||
|
}
|
||||||
|
|
||||||
scrollReady.value = false
|
|
||||||
await nextTick()
|
await nextTick()
|
||||||
|
|
||||||
rewardRestartTimer = setTimeout(async () => {
|
rewardRestartTimer = setTimeout(async () => {
|
||||||
await waitForLayoutFrame()
|
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
|
rewardRestartTimer = null
|
||||||
}, 80)
|
}, 80)
|
||||||
}
|
}
|
||||||
@ -433,6 +469,10 @@ onUnmounted(() => {
|
|||||||
clearTimeout(rewardRestartTimer)
|
clearTimeout(rewardRestartTimer)
|
||||||
rewardRestartTimer = null
|
rewardRestartTimer = null
|
||||||
}
|
}
|
||||||
|
if (rewardAnimationFrame) {
|
||||||
|
cancelAnimationFrame(rewardAnimationFrame)
|
||||||
|
rewardAnimationFrame = null
|
||||||
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -470,13 +510,17 @@ onUnmounted(() => {
|
|||||||
right: 15vw;
|
right: 15vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.reward-scroll-viewport {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
direction: ltr;
|
||||||
|
}
|
||||||
|
|
||||||
.scroll-wrapper {
|
.scroll-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
transform: translate3d(0, 0, 0);
|
|
||||||
-webkit-transform: translate3d(0, 0, 0);
|
|
||||||
backface-visibility: hidden;
|
|
||||||
-webkit-backface-visibility: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.scroll-content {
|
.scroll-content {
|
||||||
@ -485,47 +529,15 @@ onUnmounted(() => {
|
|||||||
align-items: center;
|
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 {
|
[dir='ltr'] .scroll-content {
|
||||||
margin-left: 4vw;
|
margin-left: 4vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
[dir='rtl'] .scroll-content {
|
[dir='rtl'] .scroll-content {
|
||||||
margin-left: auto;
|
direction: rtl;
|
||||||
margin-right: 4vw;
|
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) {
|
@media screen and (max-width: 360px) {
|
||||||
* {
|
* {
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user