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