Web 平台 API / 响应式图片 · 让浏览器挑对那一张 / 尺寸与占位:width、height 与 object-fit 待审核 6 / 10
width / height · aspect-ratio

尺寸与占位:width、height 与 object-fit

图片是替换元素,它的天然尺寸要等文件下载、解析出头部之后才知道。在那之前浏览器只能按某个假设的尺寸排版,假设与实际不符时,图片到达的那一刻就把下方内容推开。这类位移是 Core Web Vitals 中 CLS(Cumulative Layout Shift)的主要来源。

1 · 属性变成比例

width 与 height 是 HTML 最早的一批属性,值是不带单位的像素数,作为 presentational hint 映射到 CSS 的 width / height。流式布局兴起后,作者普遍用 CSS 覆盖掉它们:max-width: 100%; height: auto。height: auto 一写,属性里的高度就不再参与计算,图片下载前的高度退回到 0 或 alt 文本的一行,属性形同虚设。很多作者于是干脆不写,占位问题随之出现。

2019 年 CSSWG 与 WHATWG 为此给这两个属性加了第二层映射:<img width="640" height="427"> 同时产生 aspect-ratio: auto 640 / 427。Firefox 71 与 Chrome 79 在 2019-12-10 同日发布这项改动,Safari 15(2021-09)跟进(兼容性据 MDN BCD,核对于 2026-09)。流式宽度加 height: auto 时,浏览器用属性给出的比例算出高度,下载前就占好正确的位置。

规则的要点是比例而非数值。属性写成 640 × 427 还是 3 × 2,只要 CSS 宽度另有来源,占位结果相同。srcset 里有多张候选时,属性按任意一张(通常是 src 那张)的像素宽高写即可。

图 1-1 · 一张延迟 1.2 秒下载的 320px 宽图片,以及它下方段落在加载前后的位移。可切换三种尺寸声明,观察占位高度与位移量。

实测三种写法的差别很直观。不写属性时,图片下载前只占 alt 文本的一行(24px),到达后变成 213px,下方段落下移 189px。写上 640 × 427 后,加载前按比例占 214px,加载后 213px,段落上移 1px。这 1px 值得解释:候选图的真实尺寸是 320 × 213,比例 1.5023;属性里的 427 是 640×2/3=426.67640 \times 2/3 = 426.67 取整的结果,比例 1.4988。两者相差 0.2%,在 320px 宽度上正好差出一个像素的舍入。按真实文件尺寸写属性就不会有这 1px,从设计稿的比例反推时则要接受它。

2 · auto 的含义

映射出的值带一个 auto,它决定了属性写错时的行为。aspect-ratio: auto 640 / 427 的意思是:图片有天然宽高比时用天然的,没有时才用 640 / 427。下载前图片没有天然尺寸,属性比例生效;下载后图片自己的比例接管。

把属性写成 640 × 640 的正方形,占位按正方形算出 320px 高;图片到达后按 3:2 变成 213px,下方段落上移 107px。错误的属性比不写属性位移更小,但仍然是位移。picture 与 source §2 的 art direction 场景就是这种错误的常见来源:<img> 上写着方图的宽高,宽幅分支命中后,占位按方图、显示按宽幅。

有一类图片不应受 auto 影响:sizes="auto" 的图带着 UA 样式 contain: size,天然宽高比被隔离,属性比例是唯一的比例来源(见 sizes="auto" §2)。这时属性写错不会在加载后被纠正,图片会按错误的比例一直显示。

注 · Safari 14 有过一个部分实现:没有有效 src 的图片不按属性比例占位,而懒加载库恰好靠「先不给 src」来推迟下载,于是在这类页面上属性失效(据 MDN BCD 的注释与 WebKit bug 224197)。Safari 15 修正了这一点(核对于 2026-09)。

3 · 盒子比例与内容比例不一致

有时盒子的比例由布局强制给定:卡片缩略图统一 16:9、头像统一 1:1,而源图比例各不相同。CSS 同时给定宽和高后,图片默认 object-fit: fill,被拉伸到盒子的比例,内容失真。

object-fit 决定内容怎样落进盒子,它对替换元素的作用与 background-size 对背景图的作用相同:

取值 行为
fill 拉伸到盒子尺寸,比例失真(初始值)
contain 等比缩放到整图可见,留出空白
cover 等比缩放到铺满盒子,裁掉溢出部分
none 保持天然尺寸,不缩放
scale-down 取 none 与 contain 中较小的一个

object-position 决定缩放后的内容在盒子里的对齐位置,初始值 50% 50%。cover 裁掉的是哪一侧由它决定,人像构图常写 50% 20% 保住头部。

图 3-1 · 一张 3:2 的图在任意比例盒子里的五种 object-fit。可调盒宽盒高与 object-position,观察 cover 的裁切一侧随对齐位置变化。

object-fit 不影响选图,sizes 与 sizes="auto" 看到的都是盒子宽度。盒子 360 × 200 配 cover 时,3:2 的图面放大到 360 × 240、上下各裁 20px,图面宽与盒宽一致,选图没有偏差。偏差出在图面比盒子更宽的情形:一个 200 × 360 的竖盒放 3:2 横图,cover 要把图放大到 540px 宽才能铺满高度,而选图只按 200px 算。这类场景的 sizes 应当按图面宽度而不是盒宽手写。