fix: 修改bug
This commit is contained in:
parent
5208bde803
commit
63f46da2b4
@ -3,7 +3,7 @@
|
|||||||
"appid" : "__UNI__F199FF4",
|
"appid" : "__UNI__F199FF4",
|
||||||
"description" : "",
|
"description" : "",
|
||||||
"versionName" : "1.0.4",
|
"versionName" : "1.0.4",
|
||||||
"versionCode" : 101,
|
"versionCode" : 102,
|
||||||
"transformPx" : false,
|
"transformPx" : false,
|
||||||
/* 5+App特有相关 */
|
/* 5+App特有相关 */
|
||||||
"app-plus" : {
|
"app-plus" : {
|
||||||
|
|||||||
@ -10,7 +10,11 @@
|
|||||||
@touchend="onTouchEnd"
|
@touchend="onTouchEnd"
|
||||||
@touchcancel="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
|
<view
|
||||||
v-for="card in cards"
|
v-for="card in cards"
|
||||||
:key="card.id"
|
:key="card.id"
|
||||||
@ -94,6 +98,7 @@ let isComponentMounted = false // 标记组件是否已卸载
|
|||||||
let mockDataOffset = 0 // 模拟数据循环偏移量
|
let mockDataOffset = 0 // 模拟数据循环偏移量
|
||||||
let appendFailed = false // 标记追加是否已失败
|
let appendFailed = false // 标记追加是否已失败
|
||||||
let isInitialLoading = true // 标记是否在初始加载中
|
let isInitialLoading = true // 标记是否在初始加载中
|
||||||
|
let isIOS = false // 是否为 iOS 平台
|
||||||
|
|
||||||
// ========== RAF 兼容 ==========
|
// ========== RAF 兼容 ==========
|
||||||
const rafFn = (cb) => {
|
const rafFn = (cb) => {
|
||||||
@ -107,7 +112,71 @@ const cafFn = (id) => {
|
|||||||
clearTimeout(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 rafId = null
|
||||||
let userInteracting = false
|
let userInteracting = false
|
||||||
let resumeTimer = null
|
let resumeTimer = null
|
||||||
@ -115,9 +184,10 @@ let appendTimer = null // 防抖定时器
|
|||||||
let autoScrollPos = 0 // 自动滚动目标位置
|
let autoScrollPos = 0 // 自动滚动目标位置
|
||||||
let momentumTimer = null // iOS 惯性滚动检测定时器
|
let momentumTimer = null // iOS 惯性滚动检测定时器
|
||||||
let scrollUpdateTimer = null // 定期同步 scrollLeft 的定时器
|
let scrollUpdateTimer = null // 定期同步 scrollLeft 的定时器
|
||||||
|
let iosAutoScrollTimer = null // iOS 原生滚动定时器(检测惯性结束)
|
||||||
|
|
||||||
const startAutoScroll = () => {
|
const startAutoScroll = () => {
|
||||||
if (!isComponentMounted) return
|
if (!isComponentMounted || isIOS) return
|
||||||
if (rafId) {
|
if (rafId) {
|
||||||
cafFn(rafId)
|
cafFn(rafId)
|
||||||
rafId = null
|
rafId = null
|
||||||
@ -155,11 +225,13 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检测 iOS 惯性滚动是否结束
|
// 检测 Android 惯性滚动是否结束
|
||||||
let lastTouchEndPos = 0 // touchend 时的位置快照
|
let lastTouchEndPos = 0 // touchend 时的位置快照
|
||||||
|
|
||||||
const detectMomentumEnd = () => {
|
const detectMomentumEnd = () => {
|
||||||
@ -169,6 +241,7 @@ const detectMomentumEnd = () => {
|
|||||||
momentumTimer = setTimeout(() => {
|
momentumTimer = setTimeout(() => {
|
||||||
// 比较的是 lastTouchEndPos(touchend 时的位置),不受后续惯性影响
|
// 比较的是 lastTouchEndPos(touchend 时的位置),不受后续惯性影响
|
||||||
if (Math.abs(currentScrollLeft - lastTouchEndPos) < 3) {
|
if (Math.abs(currentScrollLeft - lastTouchEndPos) < 3) {
|
||||||
|
// 安卓:恢复自动滚动
|
||||||
autoScrollPos = currentScrollLeft
|
autoScrollPos = currentScrollLeft
|
||||||
startAutoScroll()
|
startAutoScroll()
|
||||||
} else {
|
} else {
|
||||||
@ -194,21 +267,60 @@ const scheduleAppend = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ========== 触摸 / 滚动事件 ==========
|
// ========== 触摸 / 滚动事件 ==========
|
||||||
const onTouchStart = () => {
|
let isManualScrolling = false // 是否正在手动滚动
|
||||||
|
|
||||||
|
const onTouchStart = (e) => {
|
||||||
|
if (isIOS) {
|
||||||
|
// iOS 设备:完全停止自动滚动
|
||||||
|
stopIOSAutoScroll()
|
||||||
|
isManualScrolling = true
|
||||||
|
clearTimeout(momentumTimer)
|
||||||
|
} else {
|
||||||
|
// 安卓设备:自动滚动停止
|
||||||
userInteracting = true
|
userInteracting = true
|
||||||
clearTimeout(momentumTimer)
|
clearTimeout(momentumTimer)
|
||||||
clearInterval(scrollUpdateTimer)
|
clearInterval(scrollUpdateTimer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onTouchEnd = () => {
|
const onTouchEnd = () => {
|
||||||
// 等待 iOS 惯性滚动自然结束,再决定是否恢复自动滚动
|
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()
|
detectMomentumEnd()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onScroll = (e) => {
|
const onScroll = (e) => {
|
||||||
if (!isComponentMounted) return
|
if (!isComponentMounted) return
|
||||||
currentScrollLeft = e.detail.scrollLeft
|
currentScrollLeft = e.detail.scrollLeft
|
||||||
|
|
||||||
|
// iOS 手动滚动时,不再更新自动滚动相关变量
|
||||||
|
if (isIOS && isManualScrolling) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const remainingScroll = totalWidth.value - currentScrollLeft - props.screenWidth
|
const remainingScroll = totalWidth.value - currentScrollLeft - props.screenWidth
|
||||||
if (remainingScroll < Math.max(totalWidth.value / 2, props.screenWidth)) {
|
if (remainingScroll < Math.max(totalWidth.value / 2, props.screenWidth)) {
|
||||||
if (!isLoadingMore && !appendFailed && !isInitialLoading && props.useMockData) {
|
if (!isLoadingMore && !appendFailed && !isInitialLoading && props.useMockData) {
|
||||||
@ -668,60 +780,134 @@ onMounted(() => {
|
|||||||
userInteracting = false
|
userInteracting = false
|
||||||
currentScrollLeft = 0
|
currentScrollLeft = 0
|
||||||
scrollLeft.value = 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
|
const containerH = props.screenHeight - props.bannerBottom
|
||||||
layout = new WaterfallLayout(containerH, props.category)
|
layout = new WaterfallLayout(containerH, props.category)
|
||||||
loadUsers().then(() => {
|
loadUsers().then(() => {
|
||||||
isInitialLoading = false
|
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 (!isComponentMounted) return
|
||||||
if (document.hidden) {
|
// 使用 scrollLeft.value 而非 currentScrollLeft,因为手动滚动时
|
||||||
stopAutoScroll()
|
// currentScrollLeft 不更新(onScroll 在 isManualScrolling 时直接 return)
|
||||||
|
autoScrollPos = scrollLeft.value
|
||||||
|
if (isIOS) {
|
||||||
|
startIOSAutoScroll()
|
||||||
} else {
|
} else {
|
||||||
// 页面重新可见时,延迟启动自动滚动,等待视图稳定
|
|
||||||
stopAutoScroll()
|
|
||||||
setTimeout(() => {
|
|
||||||
if (isComponentMounted) {
|
|
||||||
autoScrollPos = currentScrollLeft
|
|
||||||
startAutoScroll()
|
startAutoScroll()
|
||||||
}
|
}
|
||||||
}, 150)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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') {
|
if (typeof document !== 'undefined') {
|
||||||
document.addEventListener('visibilitychange', handleVisibilityChange)
|
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 属性变化(父组件控制)
|
// 监听 isActive 属性变化(父组件控制)
|
||||||
watch(() => props.isActive, (active) => {
|
watch(() => props.isActive, (active) => {
|
||||||
if (!isComponentMounted) return
|
if (!isComponentMounted) return
|
||||||
if (active) {
|
if (active) {
|
||||||
stopAutoScroll()
|
stopAllAutoScroll()
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (isComponentMounted) {
|
resumeAllAutoScroll()
|
||||||
autoScrollPos = currentScrollLeft
|
|
||||||
startAutoScroll()
|
|
||||||
}
|
|
||||||
}, 150)
|
}, 150)
|
||||||
} else {
|
} else {
|
||||||
stopAutoScroll()
|
stopAllAutoScroll()
|
||||||
}
|
}
|
||||||
}, { immediate: false })
|
}, { immediate: false })
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
isComponentMounted = false
|
isComponentMounted = false
|
||||||
stopAutoScroll()
|
stopAllAutoScroll()
|
||||||
clearTimeout(resumeTimer)
|
clearTimeout(resumeTimer)
|
||||||
clearTimeout(appendTimer)
|
clearTimeout(appendTimer)
|
||||||
clearTimeout(momentumTimer)
|
clearTimeout(momentumTimer)
|
||||||
if (typeof document !== 'undefined') {
|
if (typeof document !== 'undefined') {
|
||||||
document.removeEventListener('visibilitychange', handleVisibilityChange)
|
document.removeEventListener('visibilitychange', handleVisibilityChange)
|
||||||
}
|
}
|
||||||
|
// 移除 uni-app 全局监听
|
||||||
|
if (typeof uni !== 'undefined') {
|
||||||
|
try {
|
||||||
|
uni.offAppShow(onAppShowHandler)
|
||||||
|
uni.offAppHide(onAppHideHandler)
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(() => [props.screenHeight, props.bannerBottom], () => {
|
watch(() => [props.screenHeight, props.bannerBottom], () => {
|
||||||
@ -736,7 +922,7 @@ watch(() => [props.screenHeight, props.bannerBottom], () => {
|
|||||||
// 监听分类变化,重新加载数据
|
// 监听分类变化,重新加载数据
|
||||||
watch(() => props.category, (newCategory) => {
|
watch(() => props.category, (newCategory) => {
|
||||||
if (isComponentMounted) {
|
if (isComponentMounted) {
|
||||||
stopAutoScroll()
|
stopAllAutoScroll()
|
||||||
|
|
||||||
// 取消待执行的追加
|
// 取消待执行的追加
|
||||||
if (appendTimer) {
|
if (appendTimer) {
|
||||||
@ -766,7 +952,11 @@ watch(() => props.category, (newCategory) => {
|
|||||||
|
|
||||||
loadUsers().then(() => {
|
loadUsers().then(() => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
|
if (isIOS) {
|
||||||
|
startIOSAutoScroll()
|
||||||
|
} else {
|
||||||
startAutoScroll()
|
startAutoScroll()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -785,6 +975,10 @@ watch(() => props.category, (newCategory) => {
|
|||||||
top: 32rpx;
|
top: 32rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ios-animate {
|
||||||
|
will-change: transform;
|
||||||
|
}
|
||||||
|
|
||||||
.wf-card {
|
.wf-card {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user