universal-waterfall-layout
v1.1.0
Published
A lightweight, cross-framework waterfall layout library.
Maintainers
Readme
Universal Waterfall Layout
English | 中文
一个轻量级、高性能的瀑布流布局库,支持 Vue 3、React 和 原生 JS。
特性
- 🚀 框架无关: 核心逻辑使用原生 TypeScript 编写。
- ⚡ 轻量级: 零依赖(核心部分)。
- 🎨 响应式: 自动处理窗口大小调整。
- 📐 灵活布局: 同时支持 固定列宽(居中布局)和 固定列数(流式宽度)。
- 🖼 图片加载: 即使图片尺寸动态变化也能保持稳健布局。
- 💤 懒加载: 内置支持原生图片懒加载。
- ⏳ 状态管理: 内置加载中(骨架屏)和空状态支持。
- 🚀 虚拟列表: 支持万级数据渲染,基于 $O(\log N)$ 算法实现高性能检索。
- ⚓ 滚动锚定: 解决动态高度撑开导致的布局跳动,保持丝滑滑动体验。
- 🔌 可扩展: 易于扩展自定义动画。
安装
npm install universal-waterfall-layout
# or
pnpm add universal-waterfall-layoutVue 3 使用方法
从 /vue 子路径导入 Waterfall 组件。
<template>
<!-- 策略 1: 固定列宽 (居中布局) -->
<Waterfall
:gap="20"
:columnWidth="250"
:loading="isLoading"
:lazyload="true"
>
<div v-for="item in items" class="card">...</div>
<!-- 自定义加载插槽 -->
<template #loading>
<div>加载中...</div>
</template>
<!-- 自定义空状态插槽 -->
<template #empty>
<div>暂无数据</div>
</template>
</Waterfall>
<!-- 策略 2: 固定列数 (流式布局) -->
<Waterfall :gap="20" :columnCount="3">
<div v-for="item in items" class="card">...</div>
</Waterfall>
<!-- 策略 3: 虚拟列表模式 (万级数据极致性能) -->
<Waterfall
virtual
:items="massiveData"
:height="800"
:gap="20"
:columnWidth="200"
>
<template #item="{ item, index }">
<div class="card">
<img :src="item.image" />
<p>{{ item.title }} - {{ index }}</p>
</div>
</template>
</Waterfall>
</template>
<script setup>
import { Waterfall } from 'universal-waterfall-layout/vue';
import { ref } from 'vue';
const isLoading = ref(false);
const items = [
{ image: '...', title: 'Item 1' },
// ...
];
</script>
<style scoped>
.card {
background: #f0f0f0;
border-radius: 8px;
overflow: hidden;
/* 高度将由内容决定 */
}
</style>Vue 属性 (Props)
| 属性 | 类型 | 默认值 | 描述 |
|------|------|---------|-------------|
| gap | Number | 10 | 元素之间的间距(像素)。 |
| columnWidth | Number | - | 策略 1: 每列的固定宽度。容器将会居中显示。 |
| columnCount | Number | - | 策略 2: 固定的列数。宽度是流式的(自适应)。 |
| items | Array | [] | 可选:传入数据数组以便在变化时触发重新布局。 |
| loading | Boolean | false | 如果为真,显示加载骨架屏。 |
| lazyload | Boolean | false | 开启原生图片懒加载。 |
| reachBottomDistance | Number | 50 | 触底事件触发阈值(像素)。 |
| virtual | Boolean | false | 开启虚拟列表模式(高性能处理大数据量)。 |
| height | String \| Number | - | 设置容器高度,开启内部滚动。 |
| estimateItemHeight | Number | 300 | 虚拟列表预估高度。 |
Vue 事件 (Events)
| 事件名 | 描述 |
|-------|-------------|
| scroll-reach-bottom | 当滚动触底时触发。可用于无限滚动加载。 |
Vue 插槽 (Slots)
| 插槽 | 描述 |
|------|-------------|
| default | 主要内容项(非虚拟模式)。 |
| item | 开启虚拟模式后的单项渲染插槽。接受 { item, index } 参数。 |
| loading | 加载状态的自定义内容。 |
| empty | 空状态的自定义内容。 |
React 使用方法
从 /react 子路径导入 Waterfall 组件。
import React from 'react';
import { Waterfall } from 'universal-waterfall-layout/react';
const App = () => {
return (
// 策略 1: 固定宽度 (居中)
<Waterfall
gap={20}
columnWidth={250}
loading={false}
lazyload={true}
loadingComponent={<div>加载中...</div>}
emptyComponent={<div>暂无数据</div>}
>
{children}
</Waterfall>
// 策略 2: 固定列数 (流式)
<Waterfall gap={20} columnCount={3}>
{children}
</Waterfall>
// 策略 3: 虚拟列表模式
<Waterfall
virtual
items={massiveItems}
height={800}
gap={20}
columnWidth={200}
renderItem={(item, index) => (
<div key={index} className="card">
<img src={item.url} />
<span>{item.title}</span>
</div>
)}
/>
);
};React 属性 (Props)
| 属性 | 类型 | 默认值 | 描述 |
|------|------|---------|-------------|
| gap | number | 10 | 元素之间的间距(像素)。 |
| columnWidth | number | - | 每列的固定宽度。 |
| columnCount | number | - | 固定的列数。 |
| loading | boolean | false | 如果为真,显示加载骨架屏。 |
| lazyload | boolean | false | 开启原生图片懒加载。 |
| loadingComponent | ReactNode | - | 自定义加载状态组件。 |
| emptyComponent | ReactNode | - | 自定义空状态组件。 |
| onReachBottom | () => void | - | 滚动触底回调函数。 |
| reachBottomDistance | number | 50 | 触发触底事件的距离阈值(像素)。 |
| virtual | boolean | false | 是否开启虚拟列表模式。 |
| items | any[] | [] | 虚拟模式下必填:完整的数据数组。 |
| renderItem | (item, index) => ReactNode | - | 虚拟模式下必填:单项渲染函数。 |
| height | string \| number | - | 容器高度,设置此项后开启内部滚动。 |
| estimateItemHeight | number | 300 | 虚拟模式下的预估高度。 |
原生 JS / 核心库使用方法
如果你不想使用框架:
import { WaterFallCore } from 'universal-waterfall-layout';
const container = document.getElementById('my-container');
const waterfall = new WaterFallCore({
container: container,
gap: 15,
columnWidth: 220,
lazyload: true,
onLayout: () => {
console.log('布局已更新');
}
});
// 销毁
waterfall.destroy();API 选项
| 选项 | 类型 | 默认值 | 描述 |
|--------|------|---------|-------------|
| container | HTMLElement | 必填 | 容器元素。 |
| gap | number | 0 | 间距大小(px)。 |
| columnWidth | number | - | 选项 A: 固定列宽。如果设置了此项,columnCount 将被忽略。 |
| columnCount | number | - | 选项 B: 固定列数。宽度动态计算。 |
| itemSelector | string | 直接子元素 | 这里的 CSS 选择器用于选择子项。 |
| lazyload | boolean | false | 开启原生图片懒加载。 |
| onLayout | function | - | 布局计算完成后的回调。 |
| onReachBottom | function | - | 滚动触底回调函数。 |
| reachBottomDistance | number | 50 | 触底阈值(px)。 |
| virtual | boolean | false | 是否开启虚拟滚动模式。 |
| onRenderChange | (start, end) => void | - | 可见区域范围变化时的回调(仅限虚拟模式)。 |
| viewport | HTMLElement \| Window | window | 指定滚动视口容器。 |
| estimateItemHeight | number | 300 | 虚拟列表项的预估高度。 |
| itemCount | number | 0 | 总数据项数量(虚拟模式必填)。 |
开发
- 安装依赖:
npm install - 构建库:
npm run build
