feat: 实现纵向模式和平铺模式的双向无限滚动

问题描述:
- 纵向模式下,用户向上滑动触发懒加载后,向下滑动无法回到之前的内容
- 原因:纵向模式未启用累积模式,且缺少向上滚动加载上一页的功能

解决方案:
实现类似社交媒体的双向无限滚动机制:
- 向下滚动到 60% 时自动加载下一页(新内容)
- 向上滚动到顶部 10% 时自动加载上一页(旧内容)
- 加载上一页后自动调整滚动位置,保持用户视图不跳动

技术实现:

1. usePagination.js
   - 将 VERTICAL 模式加入累积模式判断 (line 57)
   - 实现 loadPrevPage 方法,支持加载上一页 (lines 285-306)
   - 导出 loadPrevPage 供组件使用 (line 364)

2. VirtualizedFourRowGrid.js
   - 添加 loadPrevPage prop 和 previousScrollHeight ref
   - 合并双向滚动检测逻辑 (lines 67-102):
     * 向下滚动: scrollPercentage > 0.6 触发 loadNextPage
     * 向上滚动: scrollTop < clientHeight * 0.1 触发 loadPrevPage
   - 实现滚动位置保持机制 (lines 133-161):
     * 记录加载前的 scrollHeight
     * 加载完成后计算高度差
     * 调整 scrollTop += heightDifference 保持视图位置

3. DynamicNewsCard.js
   - 从 usePagination 获取 loadPrevPage
   - 传递给 EventScrollList 组件

4. EventScrollList.js
   - 接收并传递 loadPrevPage 到 VirtualizedFourRowGrid
   - 四排模式和纵向模式均支持双向滚动

影响范围:
- 纵向模式 (vertical mode)
- 平铺模式 (four-row mode)

测试建议:
1. 切换到纵向模式
2. 向下滚动观察是否自动加载下一页
3. 向上滚动到顶部观察是否:
   - 自动加载上一页
   - 滚动位置保持不变,内容不跳动
4. 切换到平铺模式验证双向滚动同样生效

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
zdl
2025-11-05 09:48:01 +08:00
parent f919ce255a
commit c5b8fe91c3
4 changed files with 87 additions and 14 deletions

View File

@@ -53,8 +53,8 @@ export const usePagination = ({ allCachedEvents, total, cachedCount, dispatch, t
// 检查是否还有更多数据
const hasMore = cachedCount < total;
// 判断是否使用累积模式(四排模式)
const isAccumulateMode = mode === DISPLAY_MODES.FOUR_ROW;
// 判断是否使用累积模式(四排模式 + 纵向模式
const isAccumulateMode = mode === DISPLAY_MODES.FOUR_ROW || mode === DISPLAY_MODES.VERTICAL;
// 从缓存中切片获取当前页数据(过滤 null 占位符)
const currentPageEvents = useMemo(() => {
@@ -282,6 +282,29 @@ export const usePagination = ({ allCachedEvents, total, cachedCount, dispatch, t
}
}, [currentPage, totalPages, loadingPage, handlePageChange]);
// 加载上一页(用于双向无限滚动)
const loadPrevPage = useCallback(async () => {
if (currentPage <= 1 || loadingPage !== null) {
logger.debug('DynamicNewsCard', '无法加载上一页', {
currentPage,
loadingPage,
reason: currentPage <= 1 ? '已是第一页' : '正在加载中'
});
return Promise.resolve(false); // 已经是第一页或正在加载
}
const prevPage = currentPage - 1;
logger.debug('DynamicNewsCard', '懒加载:加载上一页', { currentPage, prevPage });
try {
await handlePageChange(prevPage);
return true;
} catch (error) {
logger.error('DynamicNewsCard', '懒加载上一页失败', error, { prevPage });
return false;
}
}, [currentPage, loadingPage, handlePageChange]);
// 模式切换处理
const handleModeToggle = useCallback((newMode) => {
if (newMode === mode) return;
@@ -337,6 +360,7 @@ export const usePagination = ({ allCachedEvents, total, cachedCount, dispatch, t
// 方法
handlePageChange,
handleModeToggle,
loadNextPage // 新增:加载下一页(用于无限滚动)
loadNextPage, // 新增:加载下一页(用于无限滚动)
loadPrevPage // 新增:加载上一页(用于双向无限滚动)
};
};