flex实现瀑布流的核心思路
瀑布流的本质是将多个高度不固定的元素按列排列,每一列的元素依次向下堆叠,新元素优先放入当前高度最小的列。使用flex属性实现时,我们可以将外层容器设置为横向排列的flex容器,内部创建多个纵向排列的flex子容器作为列容器,再将所有卡片元素分配到对应的列容器中,就能模拟出瀑布流的排列效果。

基础结构搭建
首先需要构建三层DOM结构:最外层是flex主容器,中间层是多个列容器,最内层是具体的卡片元素。主容器设置display: flex让列容器横向排列,列容器设置display: flex且flex-direction: column让内部卡片纵向排列。
<!-- 外层flex主容器 -->
<div class="waterfall-container">
<!-- 列容器,可创建3列 -->
<div class="waterfall-column">
<div class="card">卡片1</div>
<div class="card">卡片4</div>
</div>
<div class="waterfall-column">
<div class="card">卡片2</div>
<div class="card">卡片5</div>
</div>
<div class="waterfall-column">
<div class="card">卡片3</div>
<div class="card">卡片6</div>
</div>
</div>
CSS样式配置
接下来为各个层级的元素配置对应的flex属性,确保布局符合瀑布流的表现要求。
/* 主容器样式:横向排列列容器,设置间距 */
.waterfall-container {
display: flex;
gap: 16px;
padding: 16px;
}
/* 列容器样式:纵向排列卡片,宽度均分 */
.waterfall-column {
flex: 1;
display: flex;
flex-direction: column;
gap: 16px;
}
/* 卡片基础样式 */
.card {
background-color: #f5f5f5;
border-radius: 8px;
padding: 16px;
/* 模拟不同高度的卡片 */
height: auto;
}
/* 模拟不同卡片高度 */
.card:nth-child(1) { height: 120px; }
.card:nth-child(2) { height: 180px; }
.card:nth-child(3) { height: 150px; }
.card:nth-child(4) { height: 200px; }
.card:nth-child(5) { height: 140px; }
.card:nth-child(6) { height: 160px; }
动态分配卡片的逻辑
静态分配卡片的方式不够灵活,实际开发中通常需要动态计算各列高度,将新卡片分配到高度最小的列中。我们可以通过JavaScript实现这个分配逻辑。
// 获取所有列容器
const columns = document.querySelectorAll('.waterfall-column');
// 模拟要插入的新卡片数据,包含内容和高
const newCards = [
{ content: '新卡片1', height: 170 },
{ content: '新卡片2', height: 130 },
{ content: '新卡片3', height: 190 }
];
// 获取列的高度
function getColumnHeight(column) {
return column.offsetHeight;
}
// 找到高度最小的列的索引
function getMinHeightColumnIndex() {
let minHeight = Infinity;
let minIndex = 0;
columns.forEach((column, index) => {
const height = getColumnHeight(column);
if (height < minHeight) {
minHeight = height;
minIndex = index;
}
});
return minIndex;
}
// 插入新卡片
newCards.forEach(card => {
const minIndex = getMinHeightColumnIndex();
const cardElement = document.createElement('div');
cardElement.className = 'card';
cardElement.textContent = card.content;
cardElement.style.height = card.height + 'px';
columns[minIndex].appendChild(cardElement);
});
响应式适配处理
flex布局本身具备良好的响应式特性,我们可以通过媒体查询调整列容器的数量,适配不同屏幕尺寸。例如移动端显示2列,平板显示3列,桌面端显示4列。
/* 移动端默认2列 */
.waterfall-container {
flex-wrap: wrap;
}
.waterfall-column {
/* 移动端每列占50%宽度减去间距 */
flex: 0 0 calc(50% - 8px);
}
/* 平板及以上显示3列 */
@media (min-width: 768px) {
.waterfall-column {
flex: 1;
}
}
/* 桌面端显示4列 */
@media (min-width: 1200px) {
.waterfall-container {
gap: 20px;
}
.waterfall-column {
flex: 1;
}
}
注意事项
- 列容器的数量建议固定,避免动态增减列容器导致布局错乱
- 卡片的高度如果是动态加载的图片,需要在图片加载完成后再计算高度分配,避免高度计算错误
- 如果卡片内容包含内边距、边框,计算高度时需要包含这些属性的值
- flex布局的瀑布流实现方式不需要复杂的JavaScript计算,性能优于纯JS实现的瀑布流方案