column-count 是实现瀑布流入场动画的首选方案,因其天然支持按列流式排列、无需计算高度、不依赖 JS 且兼容 IE10+;但需用 JS 动态计算每项所属列并设置 animation-delay,配合 transform 和 opacity 实现高性能动画。

纯 CSS 实现瀑布流入场动画,关键不是「逐个下落」,而是利用 column-count 布局 + animation-delay 配合元素顺序,让每列首个元素错峰触发。直接用 grid 或 JS 控制单个 item 动画反而容易失序、卡顿、难维护。
为什么 column-count 是首选方案?
瀑布流本质是「按列填充」,而 column-count 天然支持:元素自动按列流式排列、无需计算高度、不依赖 JS、兼容性好(IE10+)。但它的入场动画不能靠 nth-child 精确选中“每列第一个”,因为 DOM 顺序 ≠ 视觉列顺序 —— 元素是竖着填的,比如 3 列时第 1、4、7 个元素才分别位于第 1 列顶部,但它们在 HTML 中是连续的。
所以必须换思路:用 JavaScript 手动为每个 item 注入基于列位置的 style 内联 delay,再配合 CSS 动画。
- 列数固定(如
column-count: 3)时,第n个元素落在第(n - 1) % 列数 + 1列 - 因此它的延迟 =
(列索引 - 1) * 0.2s(例如第 1 列 0s、第 2 列 0.2s、第 3 列 0.4s) - 动画本身用
transform: translateY+opacity,避免触发布局重排
如何给每个 item 动态加 delay?
别用 :nth-child 或 :nth-of-type —— 它们只认 DOM 顺序,不管视觉列位置。必须在 JS 渲染后,遍历所有 item 并计算其所属列:
立即学习“前端免费学习笔记(深入)”;
const container = document.querySelector('.waterfall');
const items = container.querySelectorAll('.item');
const colCount = parseInt(getComputedStyle(container).columnCount, 10) || 3;
<p>items.forEach((item, i) => {
const colIndex = (i % colCount) + 1; // 注意:这里用 i % colCount,不是 (i-1)%colCount
item.style.animationDelay = <code>${(colIndex - 1) * 0.15}s</code>;
});⚠️注意:i % colCount 是近似策略,适用于「item 数量远大于列数」且「各 item 高度差异不大」的场景;若高度悬殊(比如有特别高的 item 占满整列),实际列分布会偏移,此时需用 getBoundingClientRect() 获取 top 值排序分组,但开销大,一般项目不推荐。
CSS 动画部分怎么写才不抖?
动画必须满足三点:不触发 layout、不阻塞主线程、不因列宽变化重置:
- 用
transform: translateY(20px)替代margin-top或top - 加
will-change: transform, opacity提前告知浏览器优化(仅对频繁动画的 item) - 动画 duration 控制在 0.4–0.6s,过长易感知延迟,过短则像闪现
-
.item必须设break-inside: avoid,否则动画中途被断开到下一列,效果崩坏
.waterfall {
column-count: 3;
column-gap: 1rem;
}
.item {
break-inside: avoid;
opacity: 0;
transform: translateY(20px);
animation: fall-in 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
}
@keyframes fall-in {
to {
opacity: 1;
transform: translateY(0);
}
}移动端适配和列数切换时的坑
响应式改列数(比如从 column-count: 3 → 2)会重排所有 item,已播放的动画不会重置,导致新列里出现「已落地的 item 突然又从半空掉下来」。解决方法只有两个:
- 监听
resize,清空所有 item 的style.animationDelay,重新计算并赋值 - 更稳妥的做法:用媒体查询分离不同列数的 class,并搭配
animation-name: none临时禁用动画,等重排完成再恢复
另外,Safari 对 column-count 动画支持不稳定,尤其在 iOS 16 以下,transform 动画可能跳帧 —— 此时建议降级为 fade-in + slight scale,放弃位移。


















