feat:修改主页面滚动效果

This commit is contained in:
zerosaturation 2026-05-12 16:24:57 +08:00
parent 08225a7a79
commit 11df4cd8e9

View File

@ -4,16 +4,15 @@
:style="scrollStyle"
scroll-x
:show-scrollbar="false"
:scroll-left="scrollLeft"
:scroll-left="isIOS ? undefined : scrollLeft"
@scroll="onScroll"
@touchstart="onTouchStart"
@touchend="onTouchEnd"
@touchcancel="onTouchEnd"
>
<view
class="waterfall-inner"
:class="{ 'ios-animate': isIOS && !iosScrollPaused }"
:style="{ width: totalWidth + 'px', height: '100%' }"
class="waterfall-inner ios-css-animate"
:style="innerStyle"
>
<view
v-for="card in cards"
@ -82,7 +81,8 @@ const GAP = rpx2px(16)
const BORDER_W = rpx2px(2)
const SCALE = 0.9
const ROWS = 4
const AUTO_SCROLL_SPEED = 0.3
const AUTO_SCROLL_SPEED_ANDROID = 0.3
const AUTO_SCROLL_SPEED_IOS = 0.015
const AUTO_RESUME_DELAY = 1500
const PRELOAD_THRESHOLD = rpx2px(300)
@ -112,79 +112,33 @@ const cafFn = (id) => {
clearTimeout(id)
}
// ========== iOS ==========
// iOS 使 setInterval + scroll-left
let iosScrollTimer = null
// ========== iOS CSS ==========
// iOS 使 CSS @keyframes setInterval + scroll-left
let iosScrollPaused = false
let iosLastUpdateTime = 0
let iosLastScrollValue = 0 // scrollLeft
const IOS_SCROLL_INTERVAL = 16 // ~60fps
const IOS_MIN_DELTA = 0.5 //
const startIOSAutoScroll = () => {
console.log('[iOS] startIOSAutoScroll called, isIOS:', isIOS, 'isComponentMounted:', isComponentMounted)
if (!isComponentMounted) return
if (!isIOS) return
stopIOSAutoScroll()
if (!isComponentMounted || !isIOS) return
iosScrollPaused = false
iosLastUpdateTime = 0
iosLastScrollValue = autoScrollPos
iosScrollTimer = setInterval(() => {
if (!isComponentMounted || iosScrollPaused) return
const now = Date.now()
// 16ms
if (now - iosLastUpdateTime >= IOS_SCROLL_INTERVAL) {
iosLastUpdateTime = now
const newPos = autoScrollPos + AUTO_SCROLL_SPEED
//
if (Math.abs(newPos - iosLastScrollValue) >= IOS_MIN_DELTA) {
autoScrollPos = newPos
iosLastScrollValue = newPos
scrollLeft.value = newPos
} else {
autoScrollPos = newPos // autoScrollPos scrollLeft
}
//
const remainingScroll = totalWidth.value - autoScrollPos - props.screenWidth
if (remainingScroll < Math.max(totalWidth.value / 2, props.screenWidth)) {
if (!isLoadingMore && !appendFailed && !isInitialLoading && props.useMockData) {
appendMore()
}
}
}
}, 16)
}
const stopIOSAutoScroll = () => {
console.log('[iOS] stopIOSAutoScroll')
clearInterval(iosScrollTimer)
iosScrollTimer = null
iosScrollPaused = true
}
const pauseIOSAutoScroll = () => {
console.log('[iOS] pauseIOSAutoScroll')
iosScrollPaused = true
}
const resumeIOSAutoScroll = () => {
console.log('[iOS] resumeIOSAutoScroll')
iosScrollPaused = false
iosLastUpdateTime = 0
autoScrollPos = currentScrollLeft
iosLastScrollValue = currentScrollLeft
}
let rafId = null
let userInteracting = false
let resumeTimer = null
let appendTimer = null //
let autoScrollPos = 0 //
let momentumTimer = null // iOS
let scrollUpdateTimer = null // scrollLeft
let iosAutoScrollTimer = null // iOS
let momentumTimer = null // iOS/Android
let scrollUpdateTimer = null // scrollLeft Android
const startAutoScroll = () => {
if (!isComponentMounted || isIOS) return
@ -199,7 +153,7 @@ const startAutoScroll = () => {
clearInterval(scrollUpdateTimer)
scrollUpdateTimer = setInterval(() => {
if (!isComponentMounted || userInteracting || isLoadingMore) return
autoScrollPos += AUTO_SCROLL_SPEED
autoScrollPos += AUTO_SCROLL_SPEED_ANDROID
scrollLeft.value = autoScrollPos
//
@ -225,8 +179,6 @@ const stopAutoScroll = () => {
resumeTimer = null
clearTimeout(momentumTimer)
momentumTimer = null
clearTimeout(iosAutoScrollTimer)
iosAutoScrollTimer = null
userInteracting = false
isLoadingMore = false
}
@ -340,6 +292,23 @@ const scrollStyle = computed(() => ({
// overflow: 'visible',
}))
// iOS CSS
const innerStyle = computed(() => {
if (!isIOS) {
return { width: totalWidth.value + 'px', height: '100%' }
}
const scrollDist = totalWidth.value
// px/msiOS 使 AUTO_SCROLL_SPEED_IOS
const duration = scrollDist / AUTO_SCROLL_SPEED_IOS
return {
width: scrollDist + 'px',
height: '100%',
'--scroll-dist': -scrollDist + 'px',
'--anim-duration': duration + 'ms',
'--play-state': iosScrollPaused ? 'paused' : 'running',
}
})
// ========== ==========
// span = ROWS
// = span=ROWS span
@ -981,11 +950,14 @@ watch(() => props.category, (newCategory) => {
top: 32rpx;
}
.ios-animate {
will-change: transform;
transform: translate3d(0, 0, 0);
-webkit-overflow-scrolling: touch;
scroll-behavior: auto;
.ios-css-animate {
animation: iosAutoScroll var(--anim-duration) linear infinite;
animation-play-state: var(--play-state, running);
}
@keyframes iosAutoScroll {
from { transform: translateX(0); }
to { transform: translateX(var(--scroll-dist)); }
}
.wf-card {