回到归档
照片墙的瀑布流是 CSS 做的,一行 JS 都没有
3 分钟#CSS#设计
照片墙里既有横图(1400×900)、竖图(900×1300)也有方图(1100×1100)。要把它们排得好看,通常有两条路:
- 用 JavaScript 算出每张图的位置
- 让 CSS 自己算
第二条路只要三行。
columns 方案
.gallery {
columns: 2; /* 窄屏两栏 */
column-gap: 0.75rem;
}
@media (min-width: 640px) {
.gallery { columns: 3; }
}
.tile {
break-inside: avoid; /* 关键:别让一张图被拆到两栏 */
margin-bottom: 0.75rem;
}
原理是 CSS 多栏布局会把内容按高度尽量均匀地分配到各栏里 —— 这正好就是瀑布流想要的效果,不用自己算。
配上 Tailwind 就是:
<div class="gap-3 sm:columns-2 lg:columns-3">
<button class="mb-3 block w-full break-inside-avoid">…</button>
</div>
为什么不用 JS 方案
除了少写代码,还有一个不太被提起但很重要的好处:没有布局跳动。
JS 方案必须先渲染一遍、测量、再定位。用户看到的顺序是:
先看到图片挤成一列 → 突然跳成瀑布流
慢一点或者图多一点的时候,这一下很明显。CSS 方案在首帧就是最终布局,因为它压根没经过"中间状态"。
宽高比要提前写死
有个前提:每张图必须知道自己的宽高比,否则占位高度是 0,图片加载完会撑开容器、把下面的内容推下去 —— 还是跳。
所以内容数据里老老实实记了尺寸:
{
"type": "image",
"src": "/media/photos/p11.webp",
"width": 1400,
"height": 900
}
渲染时写成:
<button style="aspect-ratio: 1400 / 900">
容器的高度在第一帧就定死了,图片什么时候加载完都不影响布局。
想让页面不跳,靠的不是"加载得快",而是"提前知道自己长多高"。
视频也走同一套
视频元素同样能设置 aspect-ratio,所以图片和视频混在同一个网格里完全不需要特殊处理。视频额外做了两件事:
- 缩略图上
muted loop playsInline,鼠标悬停才play(),移开就暂停并回到开头 - 列表里只加载元数据(
preload="metadata"),不预加载整个视频文件
十个视频全预加载的话,页面能有好几十 MB。这种事只能靠克制。
