feat: 添加瀑布流滚动的安卓和ios的判断
This commit is contained in:
parent
b68ca3e7be
commit
5208bde803
@ -2,8 +2,8 @@
|
|||||||
"name" : "TopFans",
|
"name" : "TopFans",
|
||||||
"appid" : "__UNI__F199FF4",
|
"appid" : "__UNI__F199FF4",
|
||||||
"description" : "",
|
"description" : "",
|
||||||
"versionName" : "1.0.3",
|
"versionName" : "1.0.4",
|
||||||
"versionCode" : 100,
|
"versionCode" : 101,
|
||||||
"transformPx" : false,
|
"transformPx" : false,
|
||||||
/* 5+App特有相关 */
|
/* 5+App特有相关 */
|
||||||
"app-plus" : {
|
"app-plus" : {
|
||||||
@ -23,7 +23,8 @@
|
|||||||
"modules" : {
|
"modules" : {
|
||||||
"VideoPlayer" : {},
|
"VideoPlayer" : {},
|
||||||
"Camera" : {},
|
"Camera" : {},
|
||||||
"Speech" : {}
|
"Speech" : {},
|
||||||
|
"Push" : {}
|
||||||
},
|
},
|
||||||
/* 应用发布信息 */
|
/* 应用发布信息 */
|
||||||
"distribute" : {
|
"distribute" : {
|
||||||
@ -70,12 +71,7 @@
|
|||||||
"secretkey" : "1i5Aj8FwL3i11LYPeXMRwRWycictWq2X"
|
"secretkey" : "1i5Aj8FwL3i11LYPeXMRwRWycictWq2X"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"push" : {
|
"push" : {}
|
||||||
"unipush" : {
|
|
||||||
"version" : "2",
|
|
||||||
"offline" : false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"icons" : {
|
"icons" : {
|
||||||
"android" : {
|
"android" : {
|
||||||
|
|||||||
@ -78,8 +78,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.1
|
const AUTO_SCROLL_SPEED = 0.3
|
||||||
const AUTO_RESUME_DELAY = 1000
|
const AUTO_RESUME_DELAY = 1500
|
||||||
const PRELOAD_THRESHOLD = rpx2px(300)
|
const PRELOAD_THRESHOLD = rpx2px(300)
|
||||||
|
|
||||||
// ========== 状态 ==========
|
// ========== 状态 ==========
|
||||||
@ -112,62 +112,34 @@ 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 momentumTimer = null // iOS 惯性滚动检测定时器
|
||||||
|
let scrollUpdateTimer = null // 定期同步 scrollLeft 的定时器
|
||||||
|
|
||||||
const startAutoScroll = () => {
|
const startAutoScroll = () => {
|
||||||
if (!isComponentMounted) {
|
if (!isComponentMounted) return
|
||||||
return
|
|
||||||
}
|
|
||||||
// 确保没有重复的 RAF 在运行
|
|
||||||
if (rafId) {
|
if (rafId) {
|
||||||
cafFn(rafId)
|
cafFn(rafId)
|
||||||
rafId = null
|
rafId = null
|
||||||
}
|
}
|
||||||
// 重置滚动状态
|
|
||||||
userInteracting = false
|
userInteracting = false
|
||||||
|
autoScrollPos = currentScrollLeft
|
||||||
|
|
||||||
let lastScrollLeft = 0 // 用于检测手动滚动
|
// 使用定时器定期更新 scrollLeft
|
||||||
|
clearInterval(scrollUpdateTimer)
|
||||||
|
scrollUpdateTimer = setInterval(() => {
|
||||||
|
if (!isComponentMounted || userInteracting || isLoadingMore) return
|
||||||
|
autoScrollPos += AUTO_SCROLL_SPEED
|
||||||
|
scrollLeft.value = autoScrollPos
|
||||||
|
|
||||||
const step = () => {
|
// 预加载
|
||||||
if (!isComponentMounted) {
|
const remainingScroll = totalWidth.value - autoScrollPos - props.screenWidth
|
||||||
rafId = null
|
if (remainingScroll < Math.max(totalWidth.value / 2, props.screenWidth)) {
|
||||||
return
|
if (!isLoadingMore && !appendFailed && !isInitialLoading && props.useMockData) {
|
||||||
}
|
appendMore()
|
||||||
// 每次 RAF 循环开始时也检查一次
|
|
||||||
if (!isComponentMounted) {
|
|
||||||
rafId = null
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!userInteracting && !isLoadingMore) {
|
|
||||||
// 检测是否被手动滚动过(如果实际滚动位置和我们的不一致,说明用户手动滚了)
|
|
||||||
const actualScroll = scrollLeft.value
|
|
||||||
if (Math.abs(actualScroll - lastScrollLeft) > 5) {
|
|
||||||
// 用户手动滚了,同步位置
|
|
||||||
currentScrollLeft = actualScroll
|
|
||||||
} else {
|
|
||||||
// 正常累加
|
|
||||||
currentScrollLeft += AUTO_SCROLL_SPEED
|
|
||||||
scrollLeft.value = currentScrollLeft
|
|
||||||
lastScrollLeft = currentScrollLeft
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 预加载:剩余可滚动距离小于一半屏幕宽度时,触发追加
|
|
||||||
const remainingScroll = totalWidth.value - currentScrollLeft - props.screenWidth
|
|
||||||
if (remainingScroll < Math.max(totalWidth.value / 2, props.screenWidth)) {
|
|
||||||
// 直接调用 appendMore(不需要防抖)
|
|
||||||
// 注意:真实接口模式下不应该自动追加数据,由 has_more 控制
|
|
||||||
if (!isLoadingMore && !appendFailed && !isInitialLoading && props.useMockData) {
|
|
||||||
appendMore()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 用户交互中,记录当前滚动位置
|
|
||||||
lastScrollLeft = scrollLeft.value
|
|
||||||
currentScrollLeft = lastScrollLeft
|
|
||||||
}
|
}
|
||||||
rafId = rafFn(step)
|
}, 16)
|
||||||
}
|
|
||||||
rafId = rafFn(step)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const stopAutoScroll = () => {
|
const stopAutoScroll = () => {
|
||||||
@ -175,21 +147,41 @@ const stopAutoScroll = () => {
|
|||||||
cafFn(rafId)
|
cafFn(rafId)
|
||||||
rafId = null
|
rafId = null
|
||||||
}
|
}
|
||||||
|
clearInterval(scrollUpdateTimer)
|
||||||
|
scrollUpdateTimer = null
|
||||||
clearTimeout(appendTimer)
|
clearTimeout(appendTimer)
|
||||||
appendTimer = null
|
appendTimer = null
|
||||||
clearTimeout(resumeTimer)
|
clearTimeout(resumeTimer)
|
||||||
resumeTimer = null
|
resumeTimer = null
|
||||||
|
clearTimeout(momentumTimer)
|
||||||
|
momentumTimer = null
|
||||||
userInteracting = false
|
userInteracting = false
|
||||||
isLoadingMore = false
|
isLoadingMore = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const pauseForUser = () => {
|
// 检测 iOS 惯性滚动是否结束
|
||||||
userInteracting = true
|
let lastTouchEndPos = 0 // touchend 时的位置快照
|
||||||
clearTimeout(resumeTimer)
|
|
||||||
resumeTimer = setTimeout(() => { userInteracting = false }, AUTO_RESUME_DELAY)
|
const detectMomentumEnd = () => {
|
||||||
|
clearTimeout(momentumTimer)
|
||||||
|
lastTouchEndPos = currentScrollLeft
|
||||||
|
const tick = () => {
|
||||||
|
momentumTimer = setTimeout(() => {
|
||||||
|
// 比较的是 lastTouchEndPos(touchend 时的位置),不受后续惯性影响
|
||||||
|
if (Math.abs(currentScrollLeft - lastTouchEndPos) < 3) {
|
||||||
|
autoScrollPos = currentScrollLeft
|
||||||
|
startAutoScroll()
|
||||||
|
} else {
|
||||||
|
// 惯性中,更新快照
|
||||||
|
lastTouchEndPos = currentScrollLeft
|
||||||
|
tick()
|
||||||
|
}
|
||||||
|
}, 60)
|
||||||
|
}
|
||||||
|
tick()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 防抖:延迟 500ms 后再执行追加,避免频繁调用
|
// 防抖:延迟追加
|
||||||
const scheduleAppend = () => {
|
const scheduleAppend = () => {
|
||||||
if (!isComponentMounted || appendFailed) return
|
if (!isComponentMounted || appendFailed) return
|
||||||
if (appendTimer) clearTimeout(appendTimer)
|
if (appendTimer) clearTimeout(appendTimer)
|
||||||
@ -202,16 +194,23 @@ const scheduleAppend = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ========== 触摸 / 滚动事件 ==========
|
// ========== 触摸 / 滚动事件 ==========
|
||||||
const onTouchStart = () => pauseForUser()
|
const onTouchStart = () => {
|
||||||
const onTouchEnd = () => {}
|
userInteracting = true
|
||||||
|
clearTimeout(momentumTimer)
|
||||||
|
clearInterval(scrollUpdateTimer)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onTouchEnd = () => {
|
||||||
|
// 等待 iOS 惯性滚动自然结束,再决定是否恢复自动滚动
|
||||||
|
detectMomentumEnd()
|
||||||
|
}
|
||||||
|
|
||||||
const onScroll = (e) => {
|
const onScroll = (e) => {
|
||||||
if (!isComponentMounted) return
|
if (!isComponentMounted) return
|
||||||
currentScrollLeft = e.detail.scrollLeft
|
currentScrollLeft = e.detail.scrollLeft
|
||||||
// 预加载:剩余可滚动距离小于一半屏幕宽度时,触发追加
|
|
||||||
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)) {
|
||||||
// 注意:真实接口模式下不应该自动追加数据,由 has_more 控制
|
|
||||||
if (!isLoadingMore && !appendFailed && !isInitialLoading && props.useMockData) {
|
if (!isLoadingMore && !appendFailed && !isInitialLoading && props.useMockData) {
|
||||||
scheduleAppend()
|
scheduleAppend()
|
||||||
}
|
}
|
||||||
@ -681,12 +680,16 @@ onMounted(() => {
|
|||||||
const handleVisibilityChange = () => {
|
const handleVisibilityChange = () => {
|
||||||
if (!isComponentMounted) return
|
if (!isComponentMounted) return
|
||||||
if (document.hidden) {
|
if (document.hidden) {
|
||||||
userInteracting = true
|
stopAutoScroll()
|
||||||
} else {
|
} else {
|
||||||
userInteracting = false
|
// 页面重新可见时,延迟启动自动滚动,等待视图稳定
|
||||||
if (!rafId) {
|
stopAutoScroll()
|
||||||
startAutoScroll()
|
setTimeout(() => {
|
||||||
}
|
if (isComponentMounted) {
|
||||||
|
autoScrollPos = currentScrollLeft
|
||||||
|
startAutoScroll()
|
||||||
|
}
|
||||||
|
}, 150)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -698,12 +701,15 @@ if (typeof document !== 'undefined') {
|
|||||||
watch(() => props.isActive, (active) => {
|
watch(() => props.isActive, (active) => {
|
||||||
if (!isComponentMounted) return
|
if (!isComponentMounted) return
|
||||||
if (active) {
|
if (active) {
|
||||||
userInteracting = false
|
stopAutoScroll()
|
||||||
if (!rafId) {
|
setTimeout(() => {
|
||||||
startAutoScroll()
|
if (isComponentMounted) {
|
||||||
}
|
autoScrollPos = currentScrollLeft
|
||||||
|
startAutoScroll()
|
||||||
|
}
|
||||||
|
}, 150)
|
||||||
} else {
|
} else {
|
||||||
userInteracting = true
|
stopAutoScroll()
|
||||||
}
|
}
|
||||||
}, { immediate: false })
|
}, { immediate: false })
|
||||||
|
|
||||||
@ -712,6 +718,7 @@ onUnmounted(() => {
|
|||||||
stopAutoScroll()
|
stopAutoScroll()
|
||||||
clearTimeout(resumeTimer)
|
clearTimeout(resumeTimer)
|
||||||
clearTimeout(appendTimer)
|
clearTimeout(appendTimer)
|
||||||
|
clearTimeout(momentumTimer)
|
||||||
if (typeof document !== 'undefined') {
|
if (typeof document !== 'undefined') {
|
||||||
document.removeEventListener('visibilitychange', handleVisibilityChange)
|
document.removeEventListener('visibilitychange', handleVisibilityChange)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user