UNPKG

react-native-waterfall-list-view

Version:

基于官方 flatlist 实现的 多列 不定高 瀑布流组件 不依赖任何第三方

131 lines 4.99 kB
import React, { useRef, useState, useEffect, useImperativeHandle, memo, forwardRef, } from "react"; import { FlatList, View } from "react-native"; const WaterFallList = (props, ref) => { const { data, numColumns = 2, rowStyle, getItemLayout, onItemLayoutDone, ItemSeparatorComponent, ...otherProps } = props; const _itemHeightsRef = useRef([]); const flatListRef = useRef(null); const [update, forceUpdate] = useState(false); const [listData, changeListData] = useState([]); useEffect(() => { _itemHeightsRef.current = []; }, [numColumns]); useImperativeHandle(ref, () => { return { flatList: flatListRef.current, refreshList(offset = 0, animated = true) { _itemHeightsRef.current = []; flatListRef.current?.scrollToOffset({ offset, animated, }); }, }; }, []); useEffect(() => { if (!data) { return; } const columnHeights = new Array(numColumns).fill(0); let rowData = []; let rowIndex = 0; let rowOffsetTop = 0; const dataSource = []; data.forEach((item, index) => { /** * 选中当前高度最小的列 将元素放在高度最小的列 */ let columnIndex = 0; for (let idx = 1; idx < numColumns; idx++) { if (columnHeights[columnIndex] > columnHeights[idx]) { columnIndex = idx; break; } } const itemH = _itemHeightsRef.current[index] || 0; const offsetTop = columnHeights[columnIndex] || 0; columnHeights[columnIndex] += itemH; if (rowData.length === numColumns) { rowOffsetTop += dataSource[rowIndex]?.rowH || 0; rowData = []; rowIndex++; } rowData.push({ offsetTop, itemH, index, itemData: item, columnIndex, }); /** * 一行的高度由最高的item决定 * 获取一行中 最高的item */ const largestItem = rowData.sort((a, b) => b.itemH + b.offsetTop - (a.itemH + a.offsetTop))[0]; const rowH = largestItem.offsetTop + largestItem.itemH - rowOffsetTop; dataSource[rowIndex] = { rowIndex, rowData, rowH, rowOffsetTop, }; }); forceUpdate(false); changeListData(dataSource); }, [data, numColumns, update]); console.log("test rerender"); /** * 收集每个item的实际高度 */ const onItemHeightChange = (height, index) => { if (!data) { return; } const h = +height.toFixed(1); if (_itemHeightsRef.current[index] === h) { return; } _itemHeightsRef.current[index] = h; for (let i = 0; i < data.length; i++) { if (_itemHeightsRef.current[i] === undefined) { return; } } console.log("test 强制刷新"); /** * 所有item高度收集完毕后强制刷新页面 */ forceUpdate(!update); onItemLayoutDone && onItemLayoutDone(); }; return (<FlatList keyExtractor={(item, index) => `row_${index}`} {...otherProps} ref={flatListRef} ItemSeparatorComponent={null} horizontal={false} numColumns={1} data={listData} renderItem={({ item }) => { return (<View style={[ rowStyle ? rowStyle : { width: "100%" }, { position: "relative", height: item.rowH || 0, }, ]}> {item.rowData.map((rowItemData) => { const { offsetTop, columnIndex, index, itemH } = rowItemData; const opacity = itemH ? 1 : 0; const itemW = 100 / numColumns; return (<View key={index} style={{ position: "absolute", top: offsetTop - item.rowOffsetTop, left: `${columnIndex * itemW}%`, opacity, width: `${itemW}%`, }} onLayout={(e) => { onItemHeightChange(e.nativeEvent.layout.height, index); }}> <> {props.renderItem({ item: rowItemData, index, row: item })} {ItemSeparatorComponent && ItemSeparatorComponent()} </> </View>); })} </View>); }}/>); }; export default memo(forwardRef(WaterFallList)); //# sourceMappingURL=index.js.map