先记住这个答案
flex-direction: column 搭配 flex-wrap: wrap 时,容器必须有一个固定高度,子项从上到下排列,超过容器高度就换到下一列。每一列的高度等于容器高度,不是子项加起来的高度。所以列底部经常有空白。这是 flex 做不了真正瀑布流的根本原因——瀑布流要求每列的高度由内容决定。
- 各列高度等于容器高度
- 不是真正的瀑布流
- 列宽由最宽子项决定
column-gap控制列间距
列是怎么形成的
子项按照从上到下的顺序排列,每个子项占据一段高度。当一个子项放不进当前列剩余的空间时,它就进入下一列,从新列的顶部开始排。
每列的基本宽度由该列项目的尺寸贡献决定。align-content: stretch 是默认值,容器还有多余横向空间时,它会把余量分配给各列;原本宽度不同的列并不会因此保证等宽。要做严格等宽列,应明确设置列宽或改用 Grid。
.container {
display: flex;
flex-direction: column;
flex-wrap: wrap;
height: 200px;
width: 600px;
column-gap: 16px; /* 列间距 */
}
.item {
height: 90px;
}
/* 每列放 2 个 item(2 x 90 = 180 < 200) */
/* 第 3 个 item 放不下第一列,移到第二列;5 个 item 占 3 列 */注意列间距用 column-gap,行间距用 row-gap(在 column 下 row-gap 控制的是同列内子项之间的垂直间距)。
为什么不能做真正的瀑布流
真正的瀑布流(比如 Pinterest 那种)每列的高度由子项内容决定,新子项总是插入到最矮的列里。flex 的 column 加 wrap 做不到这一点,因为每列的高度是固定的(等于容器高度),子项只是按顺序往下排,不会自动选择最矮的列。
想实现真正的瀑布流,有几种方案:CSS columns 属性可以做简单的瀑布流但没法控制子项顺序。JavaScript 方案(比如 Masonry 库)可以动态计算每列高度。CSS Grid 的 masonry 值还在实验阶段。
容易答错的地方
- 以为
column加wrap就是瀑布流 - flex 的
column加wrap只是把子项按垂直方向排成多列,列高固定。它和瀑布流的核心区别是:子项不会自动选择最矮的列插入。 - 忽略了容器高度对换列的影响
- 如果容器没设高度或者设了
height: auto,所有子项都在一列里,不会换列。wrap只是允许换列,要真正换列必须有固定高度让子项溢出。
面试官还会怎么问?
怎么控制每列具体放几个子项?
没办法直接控制。换列是由容器高度和子项高度共同决定的。想让每列固定放 3 个,就把容器高度设成子项高度的 3 倍(加上 row-gap)。
column-gap 和 row-gap 在 column 方向下管的是什么?
column-gap 管列与列之间的水平间距(交叉轴方向)。row-gap 管同一列内子项之间的垂直间距(主轴方向)。名字有点反直觉,要对照轴的方向来理解。
CSS columns 属性和 flex 的 column 有什么区别?
CSS columns 是多列文本布局,子项从上到下排满一列后自动换列,列高由内容决定(最长的列决定容器高度)。flex 的 column 加 wrap 则是列高由容器决定,子项按固定高度换列。前者更接近瀑布流,但子项的排列顺序是「先列后行」,不能自由调整。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。