瀑布流布局是一种常见的网页排版方式,特别适合展示不规则内容(如图片、卡片等)。本文将详细介绍几种实现瀑布流的方案。
Column布局

优点:
- 浏览器原生多列布局自动平衡各列高度,渲染性能好,减少JavaScript计算和内存占用
- 无需手动计算元素放置位置
缺点:元素排序不固定,从上到下、从左到右排列
import { useState, useEffect } from "react";
export default function Home() {
const [items, setItems] = useState([]);
useEffect(() => {
const generatedItems = Array.from({ length: 10 }, (_, index) => ({
id: index + 1,
height: Math.floor(Math.random() * 300) + 50, // 增大高度差异
color: `hsl(${Math.random() * 360}, 70%, 80%)`,
}));
setItems(generatedItems);
}, []);
return (
<div className="max-w-7xl mx-auto p-5">
<h1 className="text-center text-2xl font-bold mb-8">瀑布流布局</h1>
<div className="columns-2 gap-4 w-full md:columns-4 sm:columns-3">
{items.map((item) => (
<div
key={item.id}
className="break-inside-avoid mb-4 rounded-lg p-4 shadow-md flex justify-center items-center text-2xl font-bold text-gray-800 transition-transform hover:-translate-y-1 duration-300"
style={{
height: `${item.height}px`,
backgroundColor: item.color,
}}
>
{item.id}
</div>
))}
</div>
</div>
);
}
Grid 实现布局

核心实现策略:
- 使用Grid布局创建固定数量的列:grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4
- 在JavaScript中将数据预先分组到各列
布局优势:
- 完全消除了空隙问题
- 保持了水平方向的数据顺序
缺点:这种分组计算有瑕疵,根据列分组,并没有哪一列高度小分配在哪一组。
import { useState, useEffect } from "react";
export default function Home() {
const [items, setItems] = useState([]);
const columnCount = 4; // 定义列数
useEffect(() => {
// 生成测试数据
const generatedItems = Array.from({ length: 60 }, (_, index) => ({
id: index + 1,
height: Math.floor(Math.random() * 200) + 100,
color: `hsl(${Math.random() * 360}, 70%, 80%)`,
}));
setItems(generatedItems);
}, []);
// 将数据分组到不同的列中
const columns = Array.from({ length: columnCount }, () => []);
items.forEach((item, i) => {
columns[i % columnCount].push(item);
});
return (
<div className="max-w-7xl mx-auto p-5">
<h1 className="text-center text-2xl font-bold mb-8">CSS3 瀑布流布局</h1>
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
{columns.map((column, columnIndex) => (
<div key={columnIndex} className="flex flex-col gap-4">
{column.map((item) => (
<div
key={item.id}
className="w-full rounded-lg p-4 shadow-md flex justify-center items-center text-2xl font-bold text-gray-800 transition-transform hover:-translate-y-1 duration-300"
style={{
height: `${item.height}px`,
backgroundColor: item.color,
}}
>
{item.id}
</div>
))}
</div>
))}
</div>
</div>
);
}
绝对定位

核心实现策略:
绝对定位机制
- 使用position: absolute精确控制每个元素位置
- 通过transform: translate(x, y)实现像素级精确定位
- 避免了传统网格或弹性布局可能产生的空隙问题
最短列优先算法
const minHeightCol = columnHeights.current.indexOf(Math.min(...columnHeights.current));
- 每个新元素总是放置在当前高度最低的列中
- 动态跟踪和更新每列的累积高度
- 确保所有列的高度尽可能接近,布局平衡
动态计算布局参数
- 根据容器实际宽度动态计算列宽
- 响应式调整列数:大屏4列、中屏3列、小屏2列
- 精确计算元素放置坐标,避免四舍五入误差
布局重绘响应机制
- 监听窗口resize事件自动触发重新计算
- 在窗口大小变化时重新分配所有元素位置
- 保持容器高度与内容高度同步
布局优势
无空隙视觉效果
- 完全消除了传统CSS Grid或Flexbox布局中常见的空隙问题
- 元素紧密排列,最大化利用可用空间
高度动态平衡
- 自动调整元素位置以保持各列高度平衡
- 即使部分元素高度差异较大时仍能保持整体布局协调
性能优化
- 使用CSS transform属性进行定位,利用GPU加速
- 通过useRef避免不必要的重渲染
- 只在必要时(窗口大小变化、数据更新)重新计算布局
适应性强
- 自动适应不同屏幕尺寸和方向
- 支持动态添加和移除元素
- 适用于各种内容类型和不规则高度的元素
import { useState, useEffect, useRef } from "react";
export default function Home() {
const [items, setItems] = useState([]);
const [layout, setLayout] = useState([]);
const containerRef = useRef(null);
const columns = useRef(0);
const columnWidth = useRef(0);
const columnHeights = useRef([]);
// 生成测试数据
useEffect(() => {
const generatedItems = Array.from({ length: 10 }, (_, index) => ({
id: index + 1,
height: Math.floor(Math.random() * 200) + 100,
color: `hsl(${Math.random() * 360}, 70%, 80%)`,
}));
setItems(generatedItems);
}, []);
// 计算瀑布流布局
useEffect(() => {
if (!items.length || !containerRef.current) return;
const updateLayout = () => {
// 获取容器宽度
const containerWidth = containerRef.current.clientWidth;
const gap = 16; // 间隙大小
// 计算列数和列宽
let colCount;
if (window.innerWidth >= 1024) {
colCount = 4; // 大屏幕
} else if (window.innerWidth >= 768) {
colCount = 3; // 中屏幕
} else {
colCount = 2; // 小屏幕
}
const colWidth = (containerWidth - (colCount - 1) * gap) / colCount;
columns.current = colCount;
columnWidth.current = colWidth;
columnHeights.current = Array(colCount).fill(0); // 初始化列高度
// 计算每个项目的位置
const newLayout = items.map((item) => {
// 找出高度最小的列
const minHeightCol = columnHeights.current.indexOf(
Math.min(...columnHeights.current)
);
// 计算x和y坐标
const x = minHeightCol * (colWidth + gap);
const y = columnHeights.current[minHeightCol];
// 更新该列高度
columnHeights.current[minHeightCol] += item.height + gap;
// 返回带位置信息的项目
return {
...item,
x,
y,
};
});
setLayout(newLayout);
};
// 初始计算
updateLayout();
// 监听窗口大小变化
window.addEventListener("resize", updateLayout);
return () => window.removeEventListener("resize", updateLayout);
}, [items]);
return (
<div className="max-w-7xl mx-auto p-5">
<h1 className="text-center text-2xl font-bold mb-8">瀑布流布局</h1>
<div
ref={containerRef}
className="relative w-full"
style={{
height: `${Math.max(...columnHeights.current, 100)}px`,
}}
>
{layout.map((item) => (
<div
key={item.id}
className="absolute rounded-lg p-4 shadow-md flex justify-center items-center text-2xl font-bold text-gray-800 transition-all duration-300 hover:-translate-y-1"
style={{
width: `${columnWidth.current}px`,
height: `${item.height}px`,
backgroundColor: item.color,
transform: `translate(${item.x}px, ${item.y}px)`,
}}
>
{item.id}
</div>
))}
</div>
</div>
);
}
原文链接:https://code.ifrontend.net/archives/608,转载请注明出处。
评论0