接到一个瀑布流布局的需求,网上找了一遍也没有找到合适的实现方式。无奈之下只能自己实现了。
先看效果:

实现思路
- 遍历列表创建异步任务(用任务队列管理),单个任务渲染一个卡片(计算获取最短列,然后将卡片推入最短列中)
- 使用
IntersectionObserver
监听加载,当滚动到底部时加载更多数据。
废话不多说,上代码吧
瀑布流渲染逻辑
这里进行了浅对比,如果判断是新增元素的情况,则不需要全部重新渲染,只需要将新增的数据创建对应异步任务即可
scss
watch(
[() => props.list],
([list], [prevList]) => {
// 判断是不是新增了元素
if (Array.isArray(list) && Array.isArray(prevList) && list.length > prevList.length && shallowCompareFirstNElements(list, prevList, prevList.length)) {
const newArr = list.slice(prevList.length);
distributeNewItems(newArr);
} else {
taskQueue.clear();
initColumns();
if (list.length > 0) {
distributeNewItems(list);
}
}
},
{
immediate: true,
}
)
列高度计算逻辑
ini
// 计算列高度
const updateColumnHeights = async () => {
await nextTick();
columns.value?.forEach(column => {
const element = document.getElementById(column.id);
if (element) {
const rect = element.getBoundingClientRect();
column.height = rect.height ?? 0;
}
});
};
异步任务创建逻辑
scss
// 追加新数据到瀑布流
const distributeNewItems = (newItems: any[]) => {
newItems.forEach(item => {
const task = () => new Promise(async (resolve, reject) => {
try {
await updateColumnHeights()
const shortestIndex = findShortestColumnIndex();
columns.value[shortestIndex].items.push({ data: item, imageLoaded: () => resolve('') });
if (!props.hasImage) {
resolve('');
}
} catch (err) {
console.log(err);
reject(err);
}
});
taskQueue.enqueue(task);
});
};
完整的逻辑请看可查看git仓库: github.com/KaiEi1020/w...
demo 还比较粗糙,有什么问题和建议欢迎讨论