R3.07.2unsized image layout jump设计研究

未声明尺寸会造成布局跳动

别名: 图片未设宽高 · intrinsic size 未知 · 无尺寸图片 CLS

概念解释

图片在解码出固有尺寸之前,文档流不知道该给它多高的盒子。未写 width / heightaspect-ratio 时,盒子先按零或行高塌缩,解码完成后再按固有尺寸撑开,后续节点被推走。这是未声明尺寸造成的布局跳动,迟到的信息特指这张图的固有宽高,不是任意后到模块插入文档流。后到的是“这张图有多大”,不是“这里忽然多了一块内容”。

机制

替换元素的默认尺寸来自资源的 intrinsic size。资源未就绪时,引擎没有这组数字,只能用零、alt 行盒或 CSS 里仅有的宽度去猜高度。decode 完成后 intrinsic size 到达,高度从猜值跳到真值,这是一次确定的布局。声明宽高或宽高比,等于把 intrinsic size 提前写成约束,解码只往已有盒子里填位图。只声明宽度、高度仍是 auto,仍会在解码后改高。

这与“任何迟到内容都会推节点”同属布局偏移,但归因不同:即使没有新 DOM 插入,单凭一张已在树里的 <img> 也会在解码瞬间跳动。懒加载只推迟解码时刻,不消除跳动;视口外解码引起的跳动用户可能看不见,进视口后解码则正好撞上阅读位置。

怎么研究

把同一页在“标记含宽高”和“不含”两种条件下冷加载,记录 layout-shift 是否指向该图片节点。实验室若命中磁盘缓存,解码几乎瞬间完成,跳动窗口极短,CLS 会偏低;现场慢网、未缓存时固有尺寸来得晚,跳动完整出现,这是典型的 metric mismatch。自变量:是否声明尺寸、是否懒加载、是否在视口内解码。因变量:该节点贡献的 CLS、解码完成与位移的时间差。不要用缓存命中的实验室会话代表首访图片。

边界

背景图不占文档流高度,未声明尺寸不会撑开邻居,但内容区可能在图上面“漂”。SVG 若自身带 viewBox 且 CSS 写了宽,高度可从 viewBox 推,不一定要位图像素宽高。固定高的裁切容器(正方形头像槽)已经把几何锁死,内部图未声明尺寸也不会改外盒。打印和预渲染若等图齐再出页,跳动发生在看见之前。

怎么落地

  • 每张 <img> / <video> / 嵌入框在标记里写上宽高或 aspect-ratio,与文件固有比一致。
  • 响应式图用 width/height 表达固有比,用 CSS 控制展示宽,让高度跟比走,而不是只写 width: 100%
  • 懒加载的图同样要先占位;延后解码不是延后占位。
  • 验证:禁用缓存冷加载,解码前后对比图片以下节点的 Y 坐标,应重合。现场 CLS 归因到图片节点的条目,应在补齐尺寸后消失。

延伸

  • 同组R3.07.1 尺寸与格式需按使用场景生成 · R3.07.3 自动播放消耗流量与注意力 · R3.07.4 视口外的媒体延后加载,视口内的优先加载 · R3.07.5 占位与最终图的主色接近可减少突兀 · R3.07.6 媒体降级链需覆盖解码失败与格式不支持
  • 相邻R3.05 渲染阻塞与布局抖动 · I2.01 骨架屏与转圈的选用
  • 站内检索unsized images · layout shift · decode

同组卡片

快捷操作

分享

分享当前页面

ios_share

https://hci.top/zh/handbook/R3.07.2