前言
瀑布流布局(Waterfall Layout)是一种流行的网页布局方式,特别适用于展示不同高度的内容卡片,如图片画廊、商品列表等。本文将深入分析一个完整的瀑布流实现,从核心算法到响应式设计,带你全面理解瀑布流的实现原理。
👻 Talk is cheap, show me the code:
核心设计思路
本篇文章旨在提供思路,因此代码整体上会比较潦草且随意,勿怪勿怪!
1. 整体架构
这个瀑布流实现采用了面向对象的设计模式,主要包含两个核心类:
WaterfallOptions
: 负责配置管理,包括列数和间距的响应式处理;Waterfall
: 继承自WaterfallOptions
,负责布局计算和渲染逻辑。
2. 响应式断点系统
const BREAKPOINTS = ["sx", "md", "lg", "xl", "2xl", "default"];
const BREAKPOINTS_MAP = {
sx: 640,
md: 768,
lg: 1024,
xl: 1280,
"2xl": 1536,
};
这套断点系统支持多种屏幕尺寸的适配,用户可以为不同断点设置不同的列数和间距。
核心算法解析
1. 列高度追踪算法
瀑布流的核心是维护每列的高度,并始终将新元素放置到最短的列中:
__columnsHeight = Array(this.__columnsCount).fill(0);
__minColumnIndex = 0;
// 找到最短列的索引
this.__minColumnIndex = this.__columnsHeight.indexOf(
Math.min(...this.__columnsHeight)
);
2. 元素定位计算
每个元素的位置通过以下公式计算:
// 垂直位置:当前最短列的高度
item.top = this.__columnsHeight[this.__minColumnIndex];
// 水平位置:列索引 × (元素宽度 + 间距)
item.left = this.__minColumnIndex * (this.__itemWidth + this.__gap);
3. 宽度自适应计算
元素宽度根据容器宽度和列数动态计算:
this.__itemWidth =
(this.__containerWidth - (this.__columnsCount - 1) * this.__gap) /
this.__columnsCount;
图片加载处理
异步加载策略
由于图片的高度需要在加载完成后才能确定,实现了智能的异步加载处理:
__paintChildWithImages($images, child) {
let imageCount = 0;
Array.from($images).forEach(($image) => {
let $proxy = new Image();
$proxy.src = $image.src;
$proxy.onload = (event) => {
// 根据图片原始尺寸计算显示高度
child.height += (this.__itemWidth / event.target.naturalWidth) *
event.target.naturalHeight;
imageCount++;
if (imageCount === $images.length) {
child.hasLoaded = true;
child.ratio = child.height / child.width;
this.__paintChild(child);
}
};
});
}
通过图片的原始尺寸和显示宽度,计算出准确的显示高度,确保布局的精确性。
CSS 配合实现
CSS 变量动态更新
JavaScript 通过 CSS 自定义属性与样式层进行通信:
this.__container.style.setProperty("--gap", `${this.__gap}px`);
this.__container.style.setProperty("--columns-count", this.__columnsCount);
this.__container.style.setProperty("--height", `${Math.max(...this.__columnsHeight)}px`);
流畅的动画效果
.waterfall-item {
transform: translate(var(--left, 0), var(--top, 0)) translateZ(0);
transition: box-shadow 0.2s linear, opacity 1s linear, transform 0.1s linear;
will-change: transform, opacity, visibility;
}
使用 translateZ(0)
开启硬件加速,will-change
属性优化动画性能。
响应式配置系统
灵活的配置选项
支持多种配置方式,既可以设置固定值,也可以为不同断点设置不同的值:
// 使用示例
const waterfall = new Waterfall($container, {
columns: {
sx: 3,
md: 4,
lg: 5,
xl: 6,
"2xl": 8,
default: 10
},
gap: {
sx: '0.5rem',
md: '1em',
lg: '12px',
xl: 16,
"2xl": 24,
default: '2%',
},
});
单位转换处理
支持多种 CSS 单位,并能正确转换为像素值:
__extractGapNumberValue(gap) {
if (/^\d+(.\d+)?(rem|em|%)$/.test(gap)) {
const $div = document.createElement("div");
$div.style.width = gap;
document.body.appendChild($div);
const { width } = $div.getBoundingClientRect();
document.body.removeChild($div);
return width;
}
return parseInt(gap);
}
性能优化策略
文档片段优化
使用 DocumentFragment
减少 DOM 操作次数:
const $fragment = document.createDocumentFragment();
Array.from(images).map((image, index) => {
const child = this.__initItem(image, this.__children.length + index);
this.__children.push(child);
$fragment.appendChild(child.$dom);
});
this.__container.appendChild($fragment);
延迟显示
通过 CSS 类控制元素的显示时机,提供更好的用户体验:
item.timer = setTimeout(() => {
item.$dom.classList.remove("waterfall-item__pending");
}, 300);
使用方式
// 创建瀑布流实例
const waterfall = new Waterfall(container, options);
// 添加内容
waterfall.append(images);
// 销毁实例
waterfall.destroy();
总结
这个瀑布流实现的亮点在于:
- 完整的响应式支持 - 支持多断点配置,适应各种屏幕尺寸
- 智能的图片加载处理 - 异步计算图片高度,确保布局准确性
- 优秀的性能表现 - 使用多种优化策略,保证流畅的用户体验
- 灵活的配置系统 - 支持多种单位和配置方式
- 现代化的实现方式 - 使用 ES6+ 语法,代码结构清晰
通过这种实现方式,我们可以创建出既美观又高性能的瀑布流布局,为用户提供优秀的浏览体验。无论是图片画廊、商品展示还是内容聚合页面,这套方案都能很好地满足需求。