实现一个简单的虚拟列表瀑布流
XCN Virtual Waterfall
一个简单的定宽不定高虚拟列表瀑布流组件,使用react + ts
编写,适用于react
项目
该项目已在WA - AI Platform - Frontend项目中实装
Github
https://github.com/CheNing233/XCN-VirtualWaterfall
Feature
- 支持虚拟列表
- 自定义列数
- 响应式
- 支持自定义底部元素
- 支持指定挂载滚动监听的容器
- 支持使用钩子动态更改单个项目渲染
- 支持使用钩子触发重新布局
待开发
根据宽度动态调整开关,适合停止滚动,宽度归0的场景
Usage
1. Install
npm install xcn-waterfall
2. Import
import {useXCNWaterfallItem, XCNWaterfall} from "xcn-waterfall";
3. Use
父组件使用示例
// src/example/responsiveCols.tsx
function ResponsiveCols() {
// 这里随机生成一些盒子
const [data, setData] = useState(
generateRandomObjects().map(item => {
const id = generateRandomId()
return {
id: id,
height: item.height,
width: item.width,
content: () => (
<Comp
name={id}
style={{
position: 'absolute', left: 0, top: 0, right: 0, bottom: 0,
background: item.color
}}
>
</Comp>
)
} as WaterfallItems // 这个类型定义了每个item的必要参数,包括id,height,width,content
})
)
// 这里是处理 onRequestBottomMore 事件,当请求3次后,返回空[]表示没有更多数据了
const count = useRef(0)
const handleRequestMore = async () => {
count.current++
let newData: WaterfallItems[] = []
if (count.current < 3) {
newData = generateRandomObjects().map(item => {
const id = generateRandomId()
return {
id: id,
height: item.height,
width: item.width,
content: () => (
<Comp
name={id}
style={{
position: 'absolute', left: 0, top: 0, right: 0, bottom: 0,
background: item.color
}}
/>
)
}
})
} else {
newData = [];
}
// 延时 1000 ms
await new Promise(resolve => setTimeout(resolve, 1000))
return newData as WaterfallItems[]
}
return (
<>
<XCNWaterfall
data={data}
// 响应式列数,指定在不同屏幕下的列数
columnsGroup={{
xs: 1,
sm: 2,
md: 3,
lg: 4,
}}
// 容器滚动监听的ref,可选,默认自动生成100%高度的容器包裹自身滚动
// scrollContainerRef={scrollRef}
onRequestBottomMore={handleRequestMore}
// 底部元素渲染函数,可选
bottomCompRenderFn={(reqCount: number, isLoading: boolean, isFinished: boolean) => (
<h4 style={{
color: 'yellow',
textAlign: 'center'
}}>waterfall bottom | reqCount {reqCount} | isLoading {`${isLoading}`} | isFinished {`${isFinished}`}</h4>
)}
// 容器样式,可选
style={{
width: '80vw',
height: '80vh'
}}
/>
</>
)
}
子组件使用示例
// src/example/comp.tsx
// 自定义的卡片
export function Comp(props: any) {
// 通过 useXCNWaterfallItem 获取到当前卡片的 item 数据,并修改并重新渲染该卡片
const {
item, updateItem,
initState, computedPosition, computedItemsInView, setItemsToRender
} = useXCNWaterfallItem(props.name)
/**
* update new width and height for box
* */
const updateNewBox = () => {
updateItem({
height: Math.floor(Math.random() * (1024 - 512 + 1)) + 512,
width: Math.floor(Math.random() * (1024 - 512 + 1)) + 512,
})
initState()
computedPosition()
setItemsToRender(computedItemsInView())
}
return (
<div {...props}>
{props.name}
<p>
use item {item?.count || "none"}
</p>
<button onClick={() => {
if (!item?.count) {
updateItem({
count: 1
})
} else {
updateItem({
count: item.count + 1
})
}
}}>
+
</button>
<button onClick={updateNewBox}>
set new box
</button>
</div>
)
}
Tips
- 使用
useXCNWaterfallItem
钩子动态更改单个项目渲染,比如单个卡片内的计数器 - 优先使用
columnsGroup
属性设置列数,如果没有该属性,则使用columns
属性,响应式是基于容器宽度而不是window宽度
Full Example
可查看src/example
目录
Principle
1. initState()
在全部重排之前调用
用于初始化瀑布流状态,计算列宽
2. computedPosition()
根据缓存的宽高计算所有item
的位置,排序生成用于二分查找的数据
3. computedItemsInView()
这一步使用二分查找,定位所有命中视口和缓存高度的item
4. setItemsToRender()
将第3步
计算出来的item
进行渲染
文章作者:xChenNing
文章链接:https://blog.glcn.top/archives/shi-xian-yi-ge-jian-dan-de-xu-ni-lie-biao-pu-bu-liu
版权声明:本博客所有文章除特别声明外,均采用CC BY-NC-SA 4.0 许可协议,转载请注明出处!
评论