UNPKG

@pixui-dev/pixui-react-virtualwaterfall

Version:

pixui 高性能React虚拟瀑布流组件

687 lines (686 loc) 20.4 kB
import { h, Component, JSX } from 'preact'; interface CSSProperties { [key: string]: string | number | undefined; } /** * 虚拟瀑布流列表组件的属性配置 * @interface VirtualWaterfallProps */ interface VirtualWaterfallProps { /** =================== 通用属性 =================== */ /** * 根元素id * @type {string} * @optional */ rootId?: string; /** * 根元素className * @type {string} * @optional */ rootClassName?: string; /** * 根元素style * @type {CSSProperties} * @optional */ rootStyle?: CSSProperties; /** =================== 虚拟列表相关 =================== */ /** * 内容区域style * @type {CSSProperties} * @optional */ contentStyle?: CSSProperties; /** * 输入数据列表 * @type {any[]} */ data: any[]; /** * 单个项目宽度(像素) * @type {number} */ itemWidth: number; /** * 容器高度(像素) * @type {number} */ containerHeight: number; /** * 列数 * @type {number} */ columns: number; /** * 列间距,默认10px * @type {number} * @default 10 * @optional */ gap?: number; /** * 可视区域外预加载的尺寸大小,默认0 * @type {number} * @default 0 * @optional */ overscan?: number; /** * 获取单个项目的高度 * @param {any} item - 数据项 * @param {number} index - 数据项索引 * @param {number} itemWidth - 项目宽度 * @returns {number} 项目高度 */ getItemHeight: (item: any, index: number, itemWidth: number) => number; /** * 渲染单个项目元素 * @param {any} item - 数据项 * @param {number} index - 数据项索引 * @returns {Object} 包含元素和引用的对象 * @returns {JSX.Element} returns.element - 渲染的元素 * @returns {Object} [returns.refs] - 元素引用对象 */ renderItem: (item: any, index: number) => { element: JSX.Element; refs?: { [key: string]: any; }; }; /** =================== 滚动效果相关 =================== */ /** * 滚动相关配置 * @type {VirtualListScrollProps} * @optional */ scrollProps?: VirtualListScrollProps; /** =================== 曝光回调相关 =================== */ /** * 项目曝光回调函数 * @param {any} item - 曝光的数据项 * @param {number} index - 曝光项目的索引 * @param {Object} refs - 元素引用对象列表 * @optional * @description 当项目进入可视区域时触发 */ onItemExpose?: (item: any, index: number, refs?: object) => void; /** * 项目隐藏回调函数 * @param {any} item - 隐藏的数据项 * @param {number} index - 隐藏项目的索引 * @param {Object} refs - 元素引用对象列表 * @optional * @description 当项目离开可视区域时触发 */ onItemHide?: (item: any, index: number, refs?: object) => void; /** =================== 缓存管理相关 =================== */ /** * 是否启用元素缓存复用 * @type {boolean} * @default true * @optional */ useCache?: boolean; /** * 更新已复用项目的元素内容 * @param {any} item - 数据项 * @param {Object} refs - 元素引用对象 * @optional * @description 需配合 useCache 使用,需配合 renderItem 返回的 refs 使用 */ updateItem?: (item: any, index: number, refs: { [key: string]: any; }) => void; /** * 获取项目类型,用于分类缓存 * @param {any} item - 数据项 * @returns {string} 项目类型 * @optional */ getItemType?: (item: any) => string; /** =================== 下拉刷新相关 =================== */ /** * 是否启用下拉刷新 * @type {boolean} * @default true * @optional */ hasPullDownRefresh?: () => boolean; /** * 下拉刷新阈值 * @type {number} * @default 50 * @optional */ pullDownRefreshThreshold?: number; /** * 下拉回调 * @optional */ onPullDown?: (scrollTop: number) => void; /** * 下拉刷新回调 * @optional */ onPullDownRefresh?: () => void; /** * 下拉结束回调 * @optional */ onPullDownEnd?: () => void; /** * 下拉刷新渲染区域 * @type {JSX.Element | JSX.Element[]} * @optional */ renderPullDownArea?: () => JSX.Element | JSX.Element[]; /** =================== 加载更多相关 =================== */ /** * 触底阈值,默认 100 * @type {number} * @default 100 * @optional */ loadMoreThreshold?: number; /** * 是否还有更多数据可加载 * @returns {boolean} 是否有更多数据 * @optional */ hasMore?: () => boolean; /** * 加载更多数据的回调 * @optional */ loadMore?: () => void; /** * 加载更多渲染区域 * @type {JSX.Element | JSX.Element[]} * @optional */ renderLoadMoreArea?: () => JSX.Element | JSX.Element[]; } /** * 虚拟瀑布流列表组件状态 * @interface VirtualWaterfallState */ interface VirtualWaterfallState { /** * 容器宽度 * @type {number} */ containerWidth: number; } interface VirtualListScrollProps { /** * 滚动回弹类型, 具体表现为是否可以拖动超过内容大小值 * - elastic: 触顶触底会回弹 * - clamped: 触顶触底不回弹 * - 默认值:elastic */ movementType?: 'elastic' | 'clamped'; /** * 滑动灵敏度 * - 值越大滑动越灵敏, 滑动距离会乘上滑动灵敏度 * - 默认值:1 */ scrollSensitivity?: number; /** * 是否有惯性 * - true: 滑动后会有惯性滚动 * - false: 滑动后立即停止 * - 默认值:true */ inertia?: boolean; /** * 惯性函数版本 * - 1: 倾向于unity的惯性效果 * - 2: 倾向于ue的惯性效果 * - 默认值:2 */ inertiaVersion?: number; /** * 惯性衰减率, 仅在inertiaVersion为1时生效 * - 取值范围:0(立即停止)到 1(无减速) * - 默认值:0.135 */ decelerationRate?: number; /** * 静态速度阻力, 仅在inertiaVersion为2时生效 * - 每秒速度的固定衰减值 * - 默认值:100 */ staticVelocityDrag?: number; /** * 摩擦系数, 仅在inertiaVersion为2时生效 * - 每秒速度的动态衰减值, 当前速度*frictionCoefficient * - 默认值:2.0 */ frictionCoefficient?: number; } /** * 虚拟瀑布流列表组件 * @class VirtualWaterfall * @extends {Component<VirtualWaterfallProps, VirtualWaterfallState>} * @description 高性能的虚拟瀑布流列表实现,支持大量数据的流畅滚动和元素复用 */ declare class VirtualWaterfall extends Component<VirtualWaterfallProps, VirtualWaterfallState> { static defaultProps: { loadMoreThreshold: number; pullDownRefreshThreshold: number; gap: number; overscan: number; }; /** =================== DOM 引用 =================== */ /** * 容器DOM引用 * @private * @type {Ref<HTMLDivElement>} */ containerRef: import("preact").RefObject<HTMLDivElement>; /** * 内容DOM引用 * @private * @type {Ref<HTMLDivElement>} */ contentRef: import("preact").RefObject<HTMLDivElement>; /** * 加载更多DOM引用 * @private * @type {Ref<HTMLDivElement>} */ private loadMoreRef; /** =================== 数据相关 =================== */ /** * 数据 * @public * @type {any[]} */ renderData: any[]; /** =================== 布局状态 =================== */ /** * 各列的当前高度 * @private * @type {number[]} */ private columnHeights; /** * 总内容高度 * @private * @type {number} */ private totalHeight; /** * 缓存的所有项目布局信息 * @private * @type {CachedItemLayout[]} */ private cachedLayouts; /** * 已计算布局的项目数量 * @private * @type {number} */ private layoutedItemCount; /** =================== 可视化状态 =================== */ /** * 当前可见的瀑布流项目列表 * @private * @type {WaterfallItem[]} */ private waterfallItems; /** * 索引到瀑布流项目的映射表 * @private * @type {Map<number, WaterfallItem>} */ private waterfallItemsMap; /** * 上次可见项目的索引列表,用于对比变化 * @private * @type {number[]} */ private lastVisibleIndices; /** * 当前滚动位置(不存储在state中避免触发重渲染) * @private * @type {number} */ private currentScrollTop; /** * 可视区域起始索引 * @private * @type {number} */ private visibleStartIndex; /** * 可视区域结束索引 * @private * @type {number} */ private visibleEndIndex; /** =================== 下拉刷新相关 =================== */ /** * 是否启用下拉刷新 * @private * @type {() => boolean} */ private hasPullDownRefresh; /** * 下拉刷新状态 * @private * @type {'idle' | 'pulling' | 'ready' | 'refreshing'} */ private pullDownRefreshState; /** * 下拉刷新距离 * @private * @type {number} */ private pullDownRefreshDistance; /** * 下拉刷新的最小距离阈值 * @private * @type {number} */ private pullDownRefreshThreshold; /** =================== 加载更多相关 =================== */ /** * 是否正在加载更多 * @private * @type {boolean} */ private isLoadingMore; /** =================== 缓存复用 =================== */ /** * 按类型分组的回收元素缓存池 * @private * @type {Map<string, WaterfallItem[]>} */ private recycledWaterfallItemsByType; /** * 生成唯一key的计数器 * @private * @type {number} */ private keyIndex; /** * 构造函数 * @param {VirtualWaterfallProps} props - 组件属性 */ constructor(props: VirtualWaterfallProps); /** =================== 生命周期方法 =================== */ /** * 组件挂载后的回调 * @description 计算布局和渲染可视区域 */ componentDidMount(): void; /** * 组件卸载前的回调 * @description 清理缓存 */ componentWillUnmount(): void; /** * 组件更新前的回调 * @param {VirtualWaterfallProps} nextProps - 新的属性 * @param {VirtualWaterfallState} nextState - 新的状态 * @param {any} nextContext - 新的上下文 * @description 检查数据变化并相应地更新布局 */ componentWillUpdate(nextProps: Readonly<VirtualWaterfallProps>, nextState: Readonly<VirtualWaterfallState>, nextContext: any): void; /** * 组件更新后的回调 * @param {VirtualWaterfallProps} prevProps - 旧的属性 * @param {VirtualWaterfallState} prevState - 旧的状态 * @param {any} snapshot - 快照 * @description 检查属性变化并相应地更新布局 */ componentDidUpdate(prevProps: Readonly<VirtualWaterfallProps>, prevState: Readonly<VirtualWaterfallState>, snapshot?: any): void; /** =================== 外部可调用方法 =================== */ /** * 重新初始化 * @description 重新初始化瀑布流组件,包括清理缓存、重新计算布局、重新计算可视区域、重新渲染组件 */ reset(): void; /** * 获取当前滚动位置 * @returns {number} 当前滚动位置 */ getScrollTop(): number; /** * 滚动到指定位置 * @param {number} scrollLeft - 滚动到距离左侧的位置 * @param {number} scrollTop - 滚动到距离上方的位置 */ scrollTo(scrollLeft: number, scrollTop: number): void; /** * 获取指定索引的项目 * @param {number} index - 要获取的项目索引 * @returns {any} 项目数据 */ getLayoutInfoByIndex(index: number): { x: number; y: number; width: number; height: number; columnIndex: number; index: number; }; /** * 获取可视区域项目索引的拷贝 * @returns {number[]} */ getVisibleIndices(): number[]; /** * 获取总内容高度 * @returns {number} */ getContentHeight(): number; /** * 更新指定索引的项目 * @param {number} index - 要更新的项目索引 * @param {any} item - 新的项目数据 */ updateItemByIndex(index: number, item: any): void; /** * 删除指定索引的项目 * @param {number} index - 要删除的项目索引 */ removeItemByIndex(index: number): void; /** * 添加子数据 * @param {any[]} data - 要添加的数据 */ addSubData(data: any[]): any[]; /** * 在指定索引添加子数据 * @param {any[]} data - 要添加的数据 * @param {number} index - 要添加的数据索引 * * */ addSubDataAtIndex(data: any[], index: number): void; /** * 从指定索引开始重新计算布局 * @param {number} index - 要重新计算布局的索引 */ layoutItemsFromIndex(index: any): void; /** =================== 事件处理方法 =================== */ /** * 滚动事件处理函数 * @private * @description 处理滚动事件,更新可视区域和触发加载更多 */ private handleScroll; /** * 检查下拉刷新状态 * @param {number} scrollTop - 当前滚动位置 * @description 检查下拉刷新状态,并触发下拉刷新 */ checkAndTriggerPullDownRefresh(scrollTop: number): void; /** * 检查并触发加载更多数据 * @private * @param {number} scrollTop - 当前滚动位置 * @description 当滚动接近底部时触发加载更多回调 */ private checkAndTriggerLoadMore; /** =================== 缓存管理方法 =================== */ /** * 重置所有缓存状态 * @private * @description 清空所有缓存数据,重置组件状态 */ private resetCache; /** * 获取项目类型(用于分类缓存) * @private * @param {any} item - 数据项 * @returns {string} 项目类型 * @description 如果用户提供了getItemType函数则使用,否则返回默认类型 */ private getItemType; /** * 将项目回收到缓存池中 * @private * @param {WaterfallItem} waterfallItem - 要回收的瀑布流项目 * @description 将不可见的项目回收到对应类型的缓存池中以供复用 */ private recycleElement; /** * 计算指定范围内项目的布局信息 * @private * @param {number} startIndex - 开始索引 * @param {number} targetCount - 目标数量,即需要计算的项目的数量 * @description 使用瀑布流算法计算每个项目的位置和尺寸 */ private layoutItems; /** * 计算当前可视区域内的项目 * @private * @returns {boolean} 可见项目是否发生变化 * @description 基于当前滚动位置计算需要渲染的项目列表 */ private calculateVisibleItems; /** * 获取优化的搜索范围 * @private * @param {number} columns - 列数 * @returns {Object} 搜索范围对象 * @returns {number} returns.searchStartIndex - 搜索开始索引 * @returns {number} returns.searchEndIndex - 搜索结束索引 * @description 基于上次可见范围优化搜索区域,提高性能 */ private getOptimizedSearchRange; /** * 在指定范围内查找可见项目 * @private * @param {number[]} visibleIndices - 可见项目索引数组 * @param {number} viewportTop - 视口顶部位置 * @param {number} viewportBottom - 视口底部位置 * @param {number} searchStartIndex - 搜索开始索引 * @param {number} searchEndIndex - 搜索结束索引 * @returns {Object} 可见范围对象 * @returns {number} returns.newVisibleStartIndex - 新的可见开始索引 * @returns {number} returns.newVisibleEndIndex - 新的可见结束索引 */ private findVisibleItems; /** * 全范围搜索(备用方案) * @private * @param {number[]} visibleIndices - 可见项目索引数组 * @param {number} viewportTop - 视口顶部位置 * @param {number} viewportBottom - 视口底部位置 * @returns {Object} 可见范围对象 * @returns {number} returns.newVisibleStartIndex - 新的可见开始索引 * @returns {number} returns.newVisibleEndIndex - 新的可见结束索引 * @description 当优化搜索失败时的全范围搜索备用方案 */ private fullRangeSearch; /** * 更新可见范围索引 * @private * @param {number} newVisibleStartIndex - 新的可见开始索引 * @param {number} newVisibleEndIndex - 新的可见结束索引 * @description 更新内部维护的可见范围索引值 */ private updateVisibleIndices; /** * 处理可见项目变化 * @private * @param {number[]} visibleIndices - 当前可见项目索引列表 * @param {any[]} data - 数据列表 * @returns {boolean} 是否有变化 * @description 比较新旧可见项目列表,处理进入和离开的项目 */ private handleVisibleItemsChange; /** * 处理离开可视区域的项目 * @private * @param {number[]} exitingIndices - 离开的项目索引列表 * @description 根据缓存策略处理离开可视区域的项目 */ private handleExitingItems; /** * 处理进入可视区域的项目 * @private * @param {number[]} enteringIndices - 进入的项目索引列表 * @param {any[]} data - 数据列表 * @description 为进入可视区域的项目创建或复用渲染元素 */ private handleEnteringItems; /** * 从缓存复用元素 * @private * @param {WaterfallItem[]} cachedElements - 缓存的元素列表 * @param {any} item - 数据项 * @param {CachedItemLayout} layout - 布局信息 * @param {string} itemType - 项目类型 * @param {number} index - 项目索引 * @description 从缓存池中获取元素并更新其内容和位置 */ private reuseElementFromCache; /** * 创建新元素 * @private * @param {any} item - 数据项 * @param {CachedItemLayout} layout - 布局信息 * @param {string} itemType - 项目类型 * @param {number} index - 项目索引 * @description 为新进入可视区域的项目创建渲染元素 */ private createNewElement; /** =================== 下拉刷新事件处理方法 =================== */ /** * 触发下拉刷新 * @private */ private triggerPullDownRefresh; /** * 重置下拉刷新状态 * @private */ private resetPullDownRefresh; /** * 完成下拉刷新(外部调用) * @public * @description 手动完成下拉刷新 */ finishPullDownRefresh(): void; /** * 处理滚动相关配置 * @private * @returns {VirtualListScrollProps} 滚动相关配置 */ private handleScrollProps; /** =================== 渲染方法 =================== */ /** * 渲染组件 * @returns {JSX.Element} 渲染的JSX元素 * @description 渲染虚拟瀑布流列表的主要结构 */ render(): h.JSX.Element; } export { VirtualWaterfall }; export type { VirtualWaterfallProps };