fix: 修改bug
This commit is contained in:
parent
5208bde803
commit
63f46da2b4
@ -3,7 +3,7 @@
|
||||
"appid" : "__UNI__F199FF4",
|
||||
"description" : "",
|
||||
"versionName" : "1.0.4",
|
||||
"versionCode" : 101,
|
||||
"versionCode" : 102,
|
||||
"transformPx" : false,
|
||||
/* 5+App特有相关 */
|
||||
"app-plus" : {
|
||||
|
||||
@ -10,7 +10,11 @@
|
||||
@touchend="onTouchEnd"
|
||||
@touchcancel="onTouchEnd"
|
||||
>
|
||||
<view class="waterfall-inner" :style="{ width: totalWidth + 'px', height: '100%' }">
|
||||
<view
|
||||
class="waterfall-inner"
|
||||
:class="{ 'ios-animate': isIOS && !iosScrollPaused }"
|
||||
:style="{ width: totalWidth + 'px', height: '100%' }"
|
||||
>
|
||||
<view
|
||||
v-for="card in cards"
|
||||
:key="card.id"
|
||||
@ -94,6 +98,7 @@ let isComponentMounted = false // 标记组件是否已卸载
|
||||
let mockDataOffset = 0 // 模拟数据循环偏移量
|
||||
let appendFailed = false // 标记追加是否已失败
|
||||
let isInitialLoading = true // 标记是否在初始加载中
|
||||
let isIOS = false // 是否为 iOS 平台
|
||||
|
||||
// ========== RAF 兼容 ==========
|
||||
const rafFn = (cb) => {
|
||||
@ -107,7 +112,71 @@ const cafFn = (id) => {
|
||||
clearTimeout(id)
|
||||
}
|
||||
|
||||
// ========== 自动滚动 ==========
|
||||
// ========== iOS 原生自动滚动 ==========
|
||||
// iOS 使用 setInterval + scroll-left 属性实现流畅滚动
|
||||
let iosScrollTimer = null
|
||||
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()
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
@ -115,9 +184,10 @@ let appendTimer = null // 防抖定时器
|
||||
let autoScrollPos = 0 // 自动滚动目标位置
|
||||
let momentumTimer = null // iOS 惯性滚动检测定时器
|
||||
let scrollUpdateTimer = null // 定期同步 scrollLeft 的定时器
|
||||
let iosAutoScrollTimer = null // iOS 原生滚动定时器(检测惯性结束)
|
||||
|
||||
const startAutoScroll = () => {
|
||||
if (!isComponentMounted) return
|
||||
if (!isComponentMounted || isIOS) return
|
||||
if (rafId) {
|
||||
cafFn(rafId)
|
||||
rafId = null
|
||||
@ -155,11 +225,13 @@ const stopAutoScroll = () => {
|
||||
resumeTimer = null
|
||||
clearTimeout(momentumTimer)
|
||||
momentumTimer = null
|
||||
clearTimeout(iosAutoScrollTimer)
|
||||
iosAutoScrollTimer = null
|
||||
userInteracting = false
|
||||
isLoadingMore = false
|
||||
}
|
||||
|
||||
// 检测 iOS 惯性滚动是否结束
|
||||
// 检测 Android 惯性滚动是否结束
|
||||
let lastTouchEndPos = 0 // touchend 时的位置快照
|
||||
|
||||
const detectMomentumEnd = () => {
|
||||
@ -169,6 +241,7 @@ const detectMomentumEnd = () => {
|
||||
momentumTimer = setTimeout(() => {
|
||||
// 比较的是 lastTouchEndPos(touchend 时的位置),不受后续惯性影响
|
||||
if (Math.abs(currentScrollLeft - lastTouchEndPos) < 3) {
|
||||
// 安卓:恢复自动滚动
|
||||
autoScrollPos = currentScrollLeft
|
||||
startAutoScroll()
|
||||
} else {
|
||||
@ -194,21 +267,60 @@ const scheduleAppend = () => {
|
||||
}
|
||||
|
||||
// ========== 触摸 / 滚动事件 ==========
|
||||
const onTouchStart = () => {
|
||||
userInteracting = true
|
||||
clearTimeout(momentumTimer)
|
||||
clearInterval(scrollUpdateTimer)
|
||||
let isManualScrolling = false // 是否正在手动滚动
|
||||
|
||||
const onTouchStart = (e) => {
|
||||
if (isIOS) {
|
||||
// iOS 设备:完全停止自动滚动
|
||||
stopIOSAutoScroll()
|
||||
isManualScrolling = true
|
||||
clearTimeout(momentumTimer)
|
||||
} else {
|
||||
// 安卓设备:自动滚动停止
|
||||
userInteracting = true
|
||||
clearTimeout(momentumTimer)
|
||||
clearInterval(scrollUpdateTimer)
|
||||
}
|
||||
}
|
||||
|
||||
const onTouchEnd = () => {
|
||||
// 等待 iOS 惯性滚动自然结束,再决定是否恢复自动滚动
|
||||
detectMomentumEnd()
|
||||
if (isIOS) {
|
||||
// iOS 设备:检测惯性滚动是否结束
|
||||
clearTimeout(momentumTimer)
|
||||
lastTouchEndPos = currentScrollLeft
|
||||
const tick = () => {
|
||||
momentumTimer = setTimeout(() => {
|
||||
if (Math.abs(currentScrollLeft - lastTouchEndPos) < 2) {
|
||||
// 惯性真正结束,延迟 300ms 再恢复自动滚动(确保惯性完全停止)
|
||||
clearTimeout(momentumTimer)
|
||||
momentumTimer = setTimeout(() => {
|
||||
isManualScrolling = false
|
||||
autoScrollPos = currentScrollLeft
|
||||
startIOSAutoScroll()
|
||||
}, 300)
|
||||
} else {
|
||||
// 惯性中,更新快照继续检测
|
||||
lastTouchEndPos = currentScrollLeft
|
||||
tick()
|
||||
}
|
||||
}, 80)
|
||||
}
|
||||
tick()
|
||||
} else {
|
||||
// 安卓设备:检测惯性滚动是否结束
|
||||
detectMomentumEnd()
|
||||
}
|
||||
}
|
||||
|
||||
const onScroll = (e) => {
|
||||
if (!isComponentMounted) return
|
||||
currentScrollLeft = e.detail.scrollLeft
|
||||
|
||||
// iOS 手动滚动时,不再更新自动滚动相关变量
|
||||
if (isIOS && isManualScrolling) {
|
||||
return
|
||||
}
|
||||
|
||||
const remainingScroll = totalWidth.value - currentScrollLeft - props.screenWidth
|
||||
if (remainingScroll < Math.max(totalWidth.value / 2, props.screenWidth)) {
|
||||
if (!isLoadingMore && !appendFailed && !isInitialLoading && props.useMockData) {
|
||||
@ -668,60 +780,134 @@ onMounted(() => {
|
||||
userInteracting = false
|
||||
currentScrollLeft = 0
|
||||
scrollLeft.value = 0
|
||||
// 获取设备平台信息,iOS 不使用自动滚动
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
isIOS = sysInfo.platform === 'ios'
|
||||
console.log('[WaterfallGrid] onMounted, platform:', sysInfo.platform, 'isIOS:', isIOS)
|
||||
const containerH = props.screenHeight - props.bannerBottom
|
||||
layout = new WaterfallLayout(containerH, props.category)
|
||||
loadUsers().then(() => {
|
||||
isInitialLoading = false
|
||||
nextTick(() => startAutoScroll())
|
||||
nextTick(() => {
|
||||
console.log('[WaterfallGrid] loadUsers done, calling auto scroll, isIOS:', isIOS)
|
||||
if (isIOS) {
|
||||
startIOSAutoScroll()
|
||||
} else {
|
||||
startAutoScroll()
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// 监听页面可见性变化
|
||||
const handleVisibilityChange = () => {
|
||||
// ========== 可见性控制 ==========
|
||||
const stopAllAutoScroll = () => {
|
||||
stopAutoScroll()
|
||||
stopIOSAutoScroll()
|
||||
}
|
||||
|
||||
const resumeAllAutoScroll = () => {
|
||||
// 清理所有残留的滚动/惯性定时器,确保干净启动
|
||||
clearTimeout(momentumTimer)
|
||||
momentumTimer = null
|
||||
stopAllAutoScroll()
|
||||
if (!isComponentMounted) return
|
||||
if (document.hidden) {
|
||||
stopAutoScroll()
|
||||
// 使用 scrollLeft.value 而非 currentScrollLeft,因为手动滚动时
|
||||
// currentScrollLeft 不更新(onScroll 在 isManualScrolling 时直接 return)
|
||||
autoScrollPos = scrollLeft.value
|
||||
if (isIOS) {
|
||||
startIOSAutoScroll()
|
||||
} else {
|
||||
// 页面重新可见时,延迟启动自动滚动,等待视图稳定
|
||||
stopAutoScroll()
|
||||
setTimeout(() => {
|
||||
if (isComponentMounted) {
|
||||
autoScrollPos = currentScrollLeft
|
||||
startAutoScroll()
|
||||
}
|
||||
}, 150)
|
||||
startAutoScroll()
|
||||
}
|
||||
}
|
||||
|
||||
// H5 页面可见性监听
|
||||
const handleVisibilityChange = () => {
|
||||
if (!isComponentMounted) return
|
||||
if (document.hidden) {
|
||||
stopAllAutoScroll()
|
||||
} else {
|
||||
// 页面从隐藏恢复时,momentum 已在 stopAllAutoScroll 中清掉,不需要等待
|
||||
resumeAllAutoScroll()
|
||||
}
|
||||
}
|
||||
|
||||
// uni-app 生命周期:App 进入前台/后台
|
||||
let appShowListener = null
|
||||
let appHideListener = null
|
||||
|
||||
// iOS/Android 小程序可见性监听(通过页面 onShow/onHide)
|
||||
// uni-app 生命周期:App 进入前台/后台
|
||||
const onAppShowHandler = () => {
|
||||
if (!isComponentMounted) return
|
||||
// 页面从隐藏恢复时,momentum 已在 stopAllAutoScroll 中清掉,不需要等待
|
||||
// 直接 resume,iOS 靠自己的 RAF 驱动,Android 靠 setInterval
|
||||
resumeAllAutoScroll()
|
||||
}
|
||||
|
||||
const onAppHideHandler = () => {
|
||||
if (!isComponentMounted) return
|
||||
stopAllAutoScroll()
|
||||
}
|
||||
|
||||
if (typeof uni !== 'undefined') {
|
||||
uni.onAppShow(onAppShowHandler)
|
||||
uni.onAppHide(onAppHideHandler)
|
||||
}
|
||||
|
||||
// H5 环境额外监听 visibilitychange
|
||||
if (typeof document !== 'undefined') {
|
||||
document.addEventListener('visibilitychange', handleVisibilityChange)
|
||||
}
|
||||
|
||||
// 供父组件调用的可见性控制方法(通过 defineExpose 暴露)
|
||||
const handleAppShow = () => {
|
||||
if (!isComponentMounted) return
|
||||
setTimeout(() => {
|
||||
resumeAllAutoScroll()
|
||||
}, AUTO_RESUME_DELAY)
|
||||
}
|
||||
|
||||
const handleAppHide = () => {
|
||||
if (!isComponentMounted) return
|
||||
stopAllAutoScroll()
|
||||
}
|
||||
|
||||
// 暴露方法给父组件
|
||||
defineExpose({
|
||||
handleAppShow,
|
||||
handleAppHide,
|
||||
})
|
||||
|
||||
// 监听 isActive 属性变化(父组件控制)
|
||||
watch(() => props.isActive, (active) => {
|
||||
if (!isComponentMounted) return
|
||||
if (active) {
|
||||
stopAutoScroll()
|
||||
stopAllAutoScroll()
|
||||
setTimeout(() => {
|
||||
if (isComponentMounted) {
|
||||
autoScrollPos = currentScrollLeft
|
||||
startAutoScroll()
|
||||
}
|
||||
resumeAllAutoScroll()
|
||||
}, 150)
|
||||
} else {
|
||||
stopAutoScroll()
|
||||
stopAllAutoScroll()
|
||||
}
|
||||
}, { immediate: false })
|
||||
|
||||
onUnmounted(() => {
|
||||
isComponentMounted = false
|
||||
stopAutoScroll()
|
||||
stopAllAutoScroll()
|
||||
clearTimeout(resumeTimer)
|
||||
clearTimeout(appendTimer)
|
||||
clearTimeout(momentumTimer)
|
||||
if (typeof document !== 'undefined') {
|
||||
document.removeEventListener('visibilitychange', handleVisibilityChange)
|
||||
}
|
||||
// 移除 uni-app 全局监听
|
||||
if (typeof uni !== 'undefined') {
|
||||
try {
|
||||
uni.offAppShow(onAppShowHandler)
|
||||
uni.offAppHide(onAppHideHandler)
|
||||
} catch (_) {}
|
||||
}
|
||||
})
|
||||
|
||||
watch(() => [props.screenHeight, props.bannerBottom], () => {
|
||||
@ -736,7 +922,7 @@ watch(() => [props.screenHeight, props.bannerBottom], () => {
|
||||
// 监听分类变化,重新加载数据
|
||||
watch(() => props.category, (newCategory) => {
|
||||
if (isComponentMounted) {
|
||||
stopAutoScroll()
|
||||
stopAllAutoScroll()
|
||||
|
||||
// 取消待执行的追加
|
||||
if (appendTimer) {
|
||||
@ -766,7 +952,11 @@ watch(() => props.category, (newCategory) => {
|
||||
|
||||
loadUsers().then(() => {
|
||||
nextTick(() => {
|
||||
startAutoScroll()
|
||||
if (isIOS) {
|
||||
startIOSAutoScroll()
|
||||
} else {
|
||||
startAutoScroll()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -785,6 +975,10 @@ watch(() => props.category, (newCategory) => {
|
||||
top: 32rpx;
|
||||
}
|
||||
|
||||
.ios-animate {
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.wf-card {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user