fix: 滚动bug修复
This commit is contained in:
parent
1cbb373a4f
commit
4df395b2ad
@ -56,7 +56,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
|
||||
import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'
|
||||
import { getInspirationFlowApi, getBatchOssPresignedUrlsApi } from '@/utils/api.js'
|
||||
import { doubleTapLike } from '@/utils/likeHelper.js'
|
||||
import { USE_MOCK_DATA, getMockDataByCategory, generateMockItems, calcSpan } from '../config/mockData.js'
|
||||
@ -77,7 +77,7 @@ const GAP = rpx2px(16)
|
||||
const BORDER_W = rpx2px(2)
|
||||
const SCALE = 0.9
|
||||
const ROWS = 4
|
||||
const AUTO_SCROLL_SPEED = 1.2
|
||||
const AUTO_SCROLL_SPEED = 0.5
|
||||
const AUTO_RESUME_DELAY = 2500
|
||||
const PRELOAD_THRESHOLD = rpx2px(300)
|
||||
|
||||
@ -112,24 +112,17 @@ let appendTimer = null // 防抖定时器
|
||||
|
||||
const startAutoScroll = () => {
|
||||
if (!isComponentMounted) {
|
||||
console.log('[WaterfallGrid] startAutoScroll blocked: not mounted')
|
||||
|
||||
return
|
||||
}
|
||||
if (rafId) cancelAnimationFrame(rafId)
|
||||
console.log('[WaterfallGrid] startAutoScroll started')
|
||||
|
||||
// 启动时先追加一次数据(使用模拟数据时直接追加)
|
||||
if (!isLoadingMore && props.useMockData) {
|
||||
console.log('[WaterfallGrid] startAutoScroll calling appendMore')
|
||||
appendMore()
|
||||
}
|
||||
|
||||
let lastScrollLeft = 0 // 用于检测手动滚动
|
||||
|
||||
const step = () => {
|
||||
if (!isComponentMounted) {
|
||||
rafId = null
|
||||
console.log('[WaterfallGrid] RAF stopped: not mounted')
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@ -165,7 +158,6 @@ const startAutoScroll = () => {
|
||||
}
|
||||
|
||||
const stopAutoScroll = () => {
|
||||
console.log('[WaterfallGrid] stopAutoScroll called')
|
||||
if (rafId) {
|
||||
cafFn(rafId)
|
||||
rafId = null
|
||||
@ -174,7 +166,8 @@ const stopAutoScroll = () => {
|
||||
appendTimer = null
|
||||
clearTimeout(resumeTimer)
|
||||
resumeTimer = null
|
||||
userInteracting = false // 重置用户交互状态
|
||||
userInteracting = false
|
||||
isLoadingMore = false
|
||||
}
|
||||
|
||||
const pauseForUser = () => {
|
||||
@ -217,7 +210,7 @@ const scrollStyle = computed(() => ({
|
||||
width: props.screenWidth + 'px',
|
||||
height: (props.screenHeight - props.bannerBottom) + 'px',
|
||||
zIndex: 2,
|
||||
// overflow: 'hidden',
|
||||
// overflow: 'visible',
|
||||
}))
|
||||
|
||||
// ========== 布局引擎 ==========
|
||||
@ -454,7 +447,6 @@ const batchGetPresignedUrls = async (urls) => {
|
||||
}
|
||||
|
||||
const loadUsers = async () => {
|
||||
console.log('[WaterfallGrid] loadUsers called, isComponentMounted:', isComponentMounted, 'useMockData:', props.useMockData, 'category:', props.category)
|
||||
if (!isComponentMounted) return
|
||||
|
||||
// 切换分类时重置偏移量
|
||||
@ -465,16 +457,16 @@ const loadUsers = async () => {
|
||||
|
||||
if (props.useMockData) {
|
||||
// 使用分类对应的模拟数据
|
||||
console.log('[WaterfallGrid] 使用模拟数据, category:', props.category)
|
||||
|
||||
const mockData = getMockDataByCategory(props.category)
|
||||
items = mockData.items
|
||||
// 更新偏移量
|
||||
mockDataOffset = items.length
|
||||
console.log('[WaterfallGrid] loadUsers 模拟数据加载完成, items:', items.length, 'offset:', mockDataOffset)
|
||||
|
||||
} else {
|
||||
// 使用真实API
|
||||
const res = await getInspirationFlowApi({ limit: 40, type: props.category })
|
||||
console.log('[WaterfallGrid] loadUsers got response, isComponentMounted:', isComponentMounted)
|
||||
|
||||
if (!isComponentMounted) return
|
||||
if (res.code === 200 && res.data?.items) {
|
||||
items = res.data.items
|
||||
@ -492,35 +484,29 @@ const loadUsers = async () => {
|
||||
span: item.span ?? null,
|
||||
}
|
||||
})
|
||||
console.log('[WaterfallGrid] loadUsers withData:', withData.length, 'sample likes:', withData.slice(0, 3).map(d => d.likes))
|
||||
|
||||
allUsers.value = withData
|
||||
cards.value = layout.compute(withData)
|
||||
totalWidth.value = layout.getTotalWidth()
|
||||
}
|
||||
isLoadingMore = false
|
||||
} catch (e) {
|
||||
console.error('[WaterfallGrid] 加载用户失败', e?.message ?? e)
|
||||
isLoadingMore = false
|
||||
}
|
||||
}
|
||||
|
||||
const appendMore = async () => {
|
||||
console.log('[WaterfallGrid] appendMore called', {
|
||||
isLoadingMore,
|
||||
isComponentMounted,
|
||||
useMockData: props.useMockData,
|
||||
category: props.category,
|
||||
cardsLength: cards.value.length,
|
||||
totalWidth: totalWidth.value
|
||||
})
|
||||
if (!isComponentMounted) {
|
||||
console.log('[WaterfallGrid] appendMore blocked: not mounted')
|
||||
|
||||
return
|
||||
}
|
||||
if (isLoadingMore) {
|
||||
console.log('[WaterfallGrid] appendMore blocked: already loading, waiting...')
|
||||
|
||||
// 等待一下再试
|
||||
setTimeout(() => {
|
||||
if (isComponentMounted && !isLoadingMore) {
|
||||
console.log('[WaterfallGrid] appendMore retry after wait')
|
||||
|
||||
appendMore()
|
||||
}
|
||||
}, 500)
|
||||
@ -536,7 +522,7 @@ const appendMore = async () => {
|
||||
const allItems = mockData.items
|
||||
const batchSize = 20
|
||||
|
||||
console.log('[WaterfallGrid] 模拟数据追加, offset:', mockDataOffset, 'items:', allItems.length)
|
||||
|
||||
|
||||
// 从 mockDataOffset 位置开始取 batchSize 个
|
||||
const itemsToAdd = []
|
||||
@ -556,7 +542,7 @@ const appendMore = async () => {
|
||||
mockDataOffset = mockDataOffset + batchSize
|
||||
|
||||
items = itemsToAdd
|
||||
console.log('[WaterfallGrid] 模拟数据追加完成, items:', items.length, 'next offset:', mockDataOffset)
|
||||
|
||||
} else {
|
||||
// 使用真实API
|
||||
const res = await getInspirationFlowApi({ limit: 20, type: props.category })
|
||||
@ -567,7 +553,7 @@ const appendMore = async () => {
|
||||
}
|
||||
|
||||
if (items && items.length > 0) {
|
||||
console.log('[WaterfallGrid] appendMore before:', 'cards.length:', cards.value.length, 'totalWidth:', totalWidth.value)
|
||||
|
||||
const withData = items.map((item) => {
|
||||
return {
|
||||
id: item.asset_id, // 直接使用 asset_id(已经在上面保证唯一)
|
||||
@ -578,17 +564,17 @@ const appendMore = async () => {
|
||||
span: item.span ?? null,
|
||||
}
|
||||
})
|
||||
console.log('[WaterfallGrid] appendMore withData:', withData.length)
|
||||
|
||||
const placed = layout.addCards(withData)
|
||||
console.log('[WaterfallGrid] appendMore placed:', placed.length, 'layout.curX:', layout.curX)
|
||||
|
||||
cards.value = [...cards.value, ...placed]
|
||||
totalWidth.value = layout.getTotalWidth()
|
||||
console.log('[WaterfallGrid] appendMore after:', 'cards.length:', cards.value.length, 'totalWidth:', totalWidth.value)
|
||||
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[WaterfallGrid] 追加用户失败', e?.message ?? e)
|
||||
} finally {
|
||||
console.log('[WaterfallGrid] appendMore finally, resetting isLoadingMore')
|
||||
|
||||
isLoadingMore = false
|
||||
}
|
||||
}
|
||||
@ -598,11 +584,11 @@ const cardTapTimers = {};
|
||||
|
||||
const handleCardClick = (card) => {
|
||||
|
||||
// if (cardTapTimers[card.id]) {
|
||||
if (cardTapTimers[card.id]) {
|
||||
// 第二次点击,双击点赞
|
||||
clearTimeout(cardTapTimers[card.id]);
|
||||
delete cardTapTimers[card.id];
|
||||
console.log('双击,触发动画');
|
||||
|
||||
|
||||
// 触发动画
|
||||
likingMap.value = { ...likingMap.value, [card.id]: true };
|
||||
@ -616,14 +602,14 @@ const handleCardClick = (card) => {
|
||||
uni.showToast({ title: '点赞成功', icon: 'success' });
|
||||
}
|
||||
});
|
||||
// } else {
|
||||
// // 第一次点击,单击跳转
|
||||
// console.log('单击,等待跳转');
|
||||
// cardTapTimers[card.id] = setTimeout(() => {
|
||||
// delete cardTapTimers[card.id];
|
||||
// uni.navigateTo({ url: `/pages/asset-detail/asset-detail?asset_id=${card.id}` });
|
||||
// }, 300);
|
||||
// }
|
||||
} else {
|
||||
// 第一次点击,单击跳转
|
||||
|
||||
cardTapTimers[card.id] = setTimeout(() => {
|
||||
delete cardTapTimers[card.id];
|
||||
uni.navigateTo({ url: `/pages/asset-detail/asset-detail?asset_id=${card.id}` });
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 初始化 ==========
|
||||
@ -631,12 +617,14 @@ onMounted(() => {
|
||||
isComponentMounted = true
|
||||
const containerH = props.screenHeight - props.bannerBottom
|
||||
layout = new WaterfallLayout(containerH, props.category)
|
||||
console.log('[WaterfallGrid] onMounted, starting...')
|
||||
loadUsers().then(() => startAutoScroll())
|
||||
currentScrollLeft = 0
|
||||
scrollLeft.value = 0
|
||||
loadUsers().then(() => {
|
||||
nextTick(() => startAutoScroll())
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
console.log('[WaterfallGrid] onUnmounted called!')
|
||||
isComponentMounted = false
|
||||
stopAutoScroll()
|
||||
clearTimeout(resumeTimer)
|
||||
@ -654,9 +642,7 @@ watch(() => [props.screenHeight, props.bannerBottom], () => {
|
||||
|
||||
// 监听分类变化,重新加载数据
|
||||
watch(() => props.category, (newCategory) => {
|
||||
console.log('[WaterfallGrid] category changed to:', newCategory)
|
||||
if (isComponentMounted) {
|
||||
// 先停止当前的自动滚动
|
||||
stopAutoScroll()
|
||||
|
||||
// 取消待执行的追加
|
||||
@ -664,7 +650,10 @@ watch(() => props.category, (newCategory) => {
|
||||
clearTimeout(appendTimer)
|
||||
appendTimer = null
|
||||
}
|
||||
isLoadingMore = false // 重置加载状态
|
||||
|
||||
// 重置滚动位置
|
||||
currentScrollLeft = 0
|
||||
scrollLeft.value = 0
|
||||
|
||||
// 重新创建布局(使用新的 span 阈值)
|
||||
const containerH = props.screenHeight - props.bannerBottom
|
||||
@ -672,10 +661,19 @@ watch(() => props.category, (newCategory) => {
|
||||
cards.value = []
|
||||
allUsers.value = []
|
||||
totalWidth.value = 0
|
||||
mockDataOffset = 0
|
||||
|
||||
// 先停止 RAF,确保在 loadUsers 完成前不会有滚动逻辑
|
||||
if (rafId) {
|
||||
cafFn(rafId)
|
||||
rafId = null
|
||||
}
|
||||
|
||||
loadUsers().then(() => {
|
||||
// 加载完成后启动自动滚动
|
||||
nextTick(() => {
|
||||
startAutoScroll()
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@ -689,6 +687,7 @@ watch(() => props.category, (newCategory) => {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
display: inline-block;
|
||||
top: 32rpx;
|
||||
}
|
||||
|
||||
.wf-card {
|
||||
@ -766,14 +765,14 @@ watch(() => props.category, (newCategory) => {
|
||||
}
|
||||
|
||||
.wf-like-wave-outer {
|
||||
top: -20rpx;
|
||||
left: -20rpx;
|
||||
top: -12rpx;
|
||||
left: -12rpx;
|
||||
border-width: 12rpx;
|
||||
}
|
||||
|
||||
.wf-like-wave-inner {
|
||||
top: -4rpx;
|
||||
left: -4rpx;
|
||||
top: 4rpx;
|
||||
left: 4rpx;
|
||||
border-width: 6rpx;
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
@ -788,7 +787,7 @@ watch(() => props.category, (newCategory) => {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1.25);
|
||||
transform: scale(1.2);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,7 +93,7 @@ const {
|
||||
} = useBanner()
|
||||
|
||||
// banner(216+360rpx) + tab栏(16+80rpx) + 间距(8rpx) ≈ 680rpx
|
||||
const bannerBottomPx = computed(() => Math.round(screenWidth.value / 750 * 716))
|
||||
const bannerBottomPx = computed(() => Math.round(screenWidth.value / 750 * 715))
|
||||
|
||||
// ========== Handlers ==========
|
||||
const handleCardClick = (card) => {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user