fix: 滚动bug修复

This commit is contained in:
zerosaturation 2026-05-06 10:50:40 +08:00
parent 1cbb373a4f
commit 4df395b2ad
2 changed files with 60 additions and 61 deletions

View File

@ -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,9 +661,18 @@ watch(() => props.category, (newCategory) => {
cards.value = []
allUsers.value = []
totalWidth.value = 0
mockDataOffset = 0
// RAF loadUsers
if (rafId) {
cafFn(rafId)
rafId = null
}
loadUsers().then(() => {
//
startAutoScroll()
nextTick(() => {
startAutoScroll()
})
})
}
})
@ -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;
}
}

View File

@ -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) => {