← 🧲 Flexbox · 简写语义与对齐边界 / 两端对齐图片墙:让 flex-grow 正比于 flex-basis 待审核 5 / 14
justified gallery · 实战

两端对齐图片墙:让 flex-grow 正比于 flex-basis

相册 / 画廊版式要的是:同一行图片等高、各图宽度随自身宽高比、整行两端撑满(Google 图片、Flickr 都这么排)。这页把它落到 flex-growflex-basis 上:只要令每个 item 的 flex-basisflex-grow 同时等于「该图缩到统一高度时的宽度」,整行就退化成等比缩放——行内自动等高、自动撑满。拖容器宽与基准行高,用 getBoundingClientRect 实测每行高度验证。

一句话的配方。 设基准行高 H,每张图归一化到该高度时的宽度为 wi=H×(/)w_i = H \times (宽/高)。把它同时写进两处:flex-basis: wᵢ 定起点、flex-grow: wᵢ 定瓜分权重。再开 flex-wrap: wrap 让放不下时换行。下面切到「等比撑满」模式即是此配方。

1 · 拖参数,逐行实测「行内是否等高」

每张都是真实图片(尺寸各异),左下角标 flex-grow / flex-basis 的取值(两者相等)、右上角是 getBoundingClientRect 实测高度。切到「原始宽 + wrap」可见每行右侧参差留白;切到「等比撑满」则两端对齐、且同一行各图高度一致。末行单独描白边——它正是需要特殊处理的那一行。

为什么行内会精确等高。分配公式 代入 basisi=growi=wibasis_i = grow_i = w_i:finali=wi+free×wi/Σw=wi×(1+free/Σw)=wi×kfinal_i = w_i + free \times w_i/Σw = w_i \times (1 + free/Σw) = w_i \times k。系数 k 对同一行所有 item 相同,于是每块宽度都乘同一个 k、高度 H×kH \times k 也随之一致——整行等价于等比缩放。这正是「让 flex-grow 正比于 flex-basis」的意义:把剩余空间按起点尺寸的比例分回去,形状不变、只是放大。

2 · 末行为什么要单独处理

末行 item 少、free 大,同样乘以 k 会比其他行明显高出一截。给容器加一个隐形伪元素 ::after { content: ''; flex-grow: 99999 }:它的 grow 权重远超所有 item 之和,几乎把末行的剩余空间全部吃掉,于是末行各图的 k1k \approx 1、维持基准高 H 并靠左排列。把上面「末行处理」切到「不处理」,即可看到末行被异常放大。

这是 CSS 方案的取舍。 flex 版把「分配」整件事交给浏览器,代码近乎为零、天生响应式,代价是末行只能靠 ::after 这类 trick 收尾、且无法精确指定每行行高。要精确控制(每行二分出一个行高使总宽恰等于容器宽、单独决定末行是否撑满),就回到 JS 方案:逐行 二分行高 + 绝对定位摆放,精确但需脚本与 resize 重算。

CSS Flex(本页) JS 二分 + 绝对定位
代码量 近乎为零(两行声明) 需布局算法 + resize 监听
响应式 天然(浏览器重排) 手动重算
行高精度 浏览器按 k 缩放,不指定具体行高 二分到任意精度
末行 ::after trick 收尾 可任意决定撑满 / 靠左
间距 gap 先扣除再分配(见 gap 页 自行在坐标里留出