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> </template>
<script setup> <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 { getInspirationFlowApi, getBatchOssPresignedUrlsApi } from '@/utils/api.js'
import { doubleTapLike } from '@/utils/likeHelper.js' import { doubleTapLike } from '@/utils/likeHelper.js'
import { USE_MOCK_DATA, getMockDataByCategory, generateMockItems, calcSpan } from '../config/mockData.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 BORDER_W = rpx2px(2)
const SCALE = 0.9 const SCALE = 0.9
const ROWS = 4 const ROWS = 4
const AUTO_SCROLL_SPEED = 1.2 const AUTO_SCROLL_SPEED = 0.5
const AUTO_RESUME_DELAY = 2500 const AUTO_RESUME_DELAY = 2500
const PRELOAD_THRESHOLD = rpx2px(300) const PRELOAD_THRESHOLD = rpx2px(300)
@ -112,24 +112,17 @@ let appendTimer = null // 防抖定时器
const startAutoScroll = () => { const startAutoScroll = () => {
if (!isComponentMounted) { if (!isComponentMounted) {
console.log('[WaterfallGrid] startAutoScroll blocked: not mounted')
return return
} }
if (rafId) cancelAnimationFrame(rafId) if (rafId) cancelAnimationFrame(rafId)
console.log('[WaterfallGrid] startAutoScroll started')
// 使
if (!isLoadingMore && props.useMockData) {
console.log('[WaterfallGrid] startAutoScroll calling appendMore')
appendMore()
}
let lastScrollLeft = 0 // let lastScrollLeft = 0 //
const step = () => { const step = () => {
if (!isComponentMounted) { if (!isComponentMounted) {
rafId = null rafId = null
console.log('[WaterfallGrid] RAF stopped: not mounted')
return return
} }
@ -165,7 +158,6 @@ const startAutoScroll = () => {
} }
const stopAutoScroll = () => { const stopAutoScroll = () => {
console.log('[WaterfallGrid] stopAutoScroll called')
if (rafId) { if (rafId) {
cafFn(rafId) cafFn(rafId)
rafId = null rafId = null
@ -174,7 +166,8 @@ const stopAutoScroll = () => {
appendTimer = null appendTimer = null
clearTimeout(resumeTimer) clearTimeout(resumeTimer)
resumeTimer = null resumeTimer = null
userInteracting = false // userInteracting = false
isLoadingMore = false
} }
const pauseForUser = () => { const pauseForUser = () => {
@ -217,7 +210,7 @@ const scrollStyle = computed(() => ({
width: props.screenWidth + 'px', width: props.screenWidth + 'px',
height: (props.screenHeight - props.bannerBottom) + 'px', height: (props.screenHeight - props.bannerBottom) + 'px',
zIndex: 2, zIndex: 2,
// overflow: 'hidden', // overflow: 'visible',
})) }))
// ========== ========== // ========== ==========
@ -454,7 +447,6 @@ const batchGetPresignedUrls = async (urls) => {
} }
const loadUsers = async () => { const loadUsers = async () => {
console.log('[WaterfallGrid] loadUsers called, isComponentMounted:', isComponentMounted, 'useMockData:', props.useMockData, 'category:', props.category)
if (!isComponentMounted) return if (!isComponentMounted) return
// //
@ -465,16 +457,16 @@ const loadUsers = async () => {
if (props.useMockData) { if (props.useMockData) {
// 使 // 使
console.log('[WaterfallGrid] 使用模拟数据, category:', props.category)
const mockData = getMockDataByCategory(props.category) const mockData = getMockDataByCategory(props.category)
items = mockData.items items = mockData.items
// //
mockDataOffset = items.length mockDataOffset = items.length
console.log('[WaterfallGrid] loadUsers 模拟数据加载完成, items:', items.length, 'offset:', mockDataOffset)
} else { } else {
// 使API // 使API
const res = await getInspirationFlowApi({ limit: 40, type: props.category }) const res = await getInspirationFlowApi({ limit: 40, type: props.category })
console.log('[WaterfallGrid] loadUsers got response, isComponentMounted:', isComponentMounted)
if (!isComponentMounted) return if (!isComponentMounted) return
if (res.code === 200 && res.data?.items) { if (res.code === 200 && res.data?.items) {
items = res.data.items items = res.data.items
@ -492,35 +484,29 @@ const loadUsers = async () => {
span: item.span ?? null, span: item.span ?? null,
} }
}) })
console.log('[WaterfallGrid] loadUsers withData:', withData.length, 'sample likes:', withData.slice(0, 3).map(d => d.likes))
allUsers.value = withData allUsers.value = withData
cards.value = layout.compute(withData) cards.value = layout.compute(withData)
totalWidth.value = layout.getTotalWidth() totalWidth.value = layout.getTotalWidth()
} }
isLoadingMore = false
} catch (e) { } catch (e) {
console.error('[WaterfallGrid] 加载用户失败', e?.message ?? e) console.error('[WaterfallGrid] 加载用户失败', e?.message ?? e)
isLoadingMore = false
} }
} }
const appendMore = async () => { 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) { if (!isComponentMounted) {
console.log('[WaterfallGrid] appendMore blocked: not mounted')
return return
} }
if (isLoadingMore) { if (isLoadingMore) {
console.log('[WaterfallGrid] appendMore blocked: already loading, waiting...')
// //
setTimeout(() => { setTimeout(() => {
if (isComponentMounted && !isLoadingMore) { if (isComponentMounted && !isLoadingMore) {
console.log('[WaterfallGrid] appendMore retry after wait')
appendMore() appendMore()
} }
}, 500) }, 500)
@ -536,7 +522,7 @@ const appendMore = async () => {
const allItems = mockData.items const allItems = mockData.items
const batchSize = 20 const batchSize = 20
console.log('[WaterfallGrid] 模拟数据追加, offset:', mockDataOffset, 'items:', allItems.length)
// mockDataOffset batchSize // mockDataOffset batchSize
const itemsToAdd = [] const itemsToAdd = []
@ -556,7 +542,7 @@ const appendMore = async () => {
mockDataOffset = mockDataOffset + batchSize mockDataOffset = mockDataOffset + batchSize
items = itemsToAdd items = itemsToAdd
console.log('[WaterfallGrid] 模拟数据追加完成, items:', items.length, 'next offset:', mockDataOffset)
} else { } else {
// 使API // 使API
const res = await getInspirationFlowApi({ limit: 20, type: props.category }) const res = await getInspirationFlowApi({ limit: 20, type: props.category })
@ -567,7 +553,7 @@ const appendMore = async () => {
} }
if (items && items.length > 0) { if (items && items.length > 0) {
console.log('[WaterfallGrid] appendMore before:', 'cards.length:', cards.value.length, 'totalWidth:', totalWidth.value)
const withData = items.map((item) => { const withData = items.map((item) => {
return { return {
id: item.asset_id, // 使 asset_id id: item.asset_id, // 使 asset_id
@ -578,17 +564,17 @@ const appendMore = async () => {
span: item.span ?? null, span: item.span ?? null,
} }
}) })
console.log('[WaterfallGrid] appendMore withData:', withData.length)
const placed = layout.addCards(withData) const placed = layout.addCards(withData)
console.log('[WaterfallGrid] appendMore placed:', placed.length, 'layout.curX:', layout.curX)
cards.value = [...cards.value, ...placed] cards.value = [...cards.value, ...placed]
totalWidth.value = layout.getTotalWidth() totalWidth.value = layout.getTotalWidth()
console.log('[WaterfallGrid] appendMore after:', 'cards.length:', cards.value.length, 'totalWidth:', totalWidth.value)
} }
} catch (e) { } catch (e) {
console.error('[WaterfallGrid] 追加用户失败', e?.message ?? e) console.error('[WaterfallGrid] 追加用户失败', e?.message ?? e)
} finally { } finally {
console.log('[WaterfallGrid] appendMore finally, resetting isLoadingMore')
isLoadingMore = false isLoadingMore = false
} }
} }
@ -598,11 +584,11 @@ const cardTapTimers = {};
const handleCardClick = (card) => { const handleCardClick = (card) => {
// if (cardTapTimers[card.id]) { if (cardTapTimers[card.id]) {
// //
clearTimeout(cardTapTimers[card.id]); clearTimeout(cardTapTimers[card.id]);
delete cardTapTimers[card.id]; delete cardTapTimers[card.id];
console.log('双击,触发动画');
// //
likingMap.value = { ...likingMap.value, [card.id]: true }; likingMap.value = { ...likingMap.value, [card.id]: true };
@ -616,14 +602,14 @@ const handleCardClick = (card) => {
uni.showToast({ title: '点赞成功', icon: 'success' }); uni.showToast({ title: '点赞成功', icon: 'success' });
} }
}); });
// } else { } else {
// // //
// console.log('');
// cardTapTimers[card.id] = setTimeout(() => { cardTapTimers[card.id] = setTimeout(() => {
// delete cardTapTimers[card.id]; delete cardTapTimers[card.id];
// uni.navigateTo({ url: `/pages/asset-detail/asset-detail?asset_id=${card.id}` }); uni.navigateTo({ url: `/pages/asset-detail/asset-detail?asset_id=${card.id}` });
// }, 300); }, 300);
// } }
} }
// ========== ========== // ========== ==========
@ -631,12 +617,14 @@ onMounted(() => {
isComponentMounted = true isComponentMounted = true
const containerH = props.screenHeight - props.bannerBottom const containerH = props.screenHeight - props.bannerBottom
layout = new WaterfallLayout(containerH, props.category) layout = new WaterfallLayout(containerH, props.category)
console.log('[WaterfallGrid] onMounted, starting...') currentScrollLeft = 0
loadUsers().then(() => startAutoScroll()) scrollLeft.value = 0
loadUsers().then(() => {
nextTick(() => startAutoScroll())
})
}) })
onUnmounted(() => { onUnmounted(() => {
console.log('[WaterfallGrid] onUnmounted called!')
isComponentMounted = false isComponentMounted = false
stopAutoScroll() stopAutoScroll()
clearTimeout(resumeTimer) clearTimeout(resumeTimer)
@ -654,9 +642,7 @@ watch(() => [props.screenHeight, props.bannerBottom], () => {
// //
watch(() => props.category, (newCategory) => { watch(() => props.category, (newCategory) => {
console.log('[WaterfallGrid] category changed to:', newCategory)
if (isComponentMounted) { if (isComponentMounted) {
//
stopAutoScroll() stopAutoScroll()
// //
@ -664,7 +650,10 @@ watch(() => props.category, (newCategory) => {
clearTimeout(appendTimer) clearTimeout(appendTimer)
appendTimer = null appendTimer = null
} }
isLoadingMore = false //
//
currentScrollLeft = 0
scrollLeft.value = 0
// 使 span // 使 span
const containerH = props.screenHeight - props.bannerBottom const containerH = props.screenHeight - props.bannerBottom
@ -672,9 +661,18 @@ watch(() => props.category, (newCategory) => {
cards.value = [] cards.value = []
allUsers.value = [] allUsers.value = []
totalWidth.value = 0 totalWidth.value = 0
mockDataOffset = 0
// RAF loadUsers
if (rafId) {
cafFn(rafId)
rafId = null
}
loadUsers().then(() => { loadUsers().then(() => {
// nextTick(() => {
startAutoScroll() startAutoScroll()
})
}) })
} }
}) })
@ -689,6 +687,7 @@ watch(() => props.category, (newCategory) => {
position: relative; position: relative;
height: 100%; height: 100%;
display: inline-block; display: inline-block;
top: 32rpx;
} }
.wf-card { .wf-card {
@ -766,14 +765,14 @@ watch(() => props.category, (newCategory) => {
} }
.wf-like-wave-outer { .wf-like-wave-outer {
top: -20rpx; top: -12rpx;
left: -20rpx; left: -12rpx;
border-width: 12rpx; border-width: 12rpx;
} }
.wf-like-wave-inner { .wf-like-wave-inner {
top: -4rpx; top: 4rpx;
left: -4rpx; left: 4rpx;
border-width: 6rpx; border-width: 6rpx;
animation-delay: 0.2s; animation-delay: 0.2s;
} }
@ -788,7 +787,7 @@ watch(() => props.category, (newCategory) => {
opacity: 1; opacity: 1;
} }
100% { 100% {
transform: scale(1.25); transform: scale(1.2);
opacity: 0; opacity: 0;
} }
} }

View File

@ -93,7 +93,7 @@ const {
} = useBanner() } = useBanner()
// banner(216+360rpx) + tab(16+80rpx) + (8rpx) 680rpx // 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 ========== // ========== Handlers ==========
const handleCardClick = (card) => { const handleCardClick = (card) => {