feat:修改主页面滚动效果
This commit is contained in:
parent
08225a7a79
commit
11df4cd8e9
@ -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/ms,iOS 使用 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 {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user