图片淡入需等资源就绪再触发opacity变化,最稳方案是onload+class切换,配合宽高/aspect-ratio预留空间、decode()精准解码(带降级)、intersectionobserver懒加载及error兜底。

图片淡入效果不是“加个 transition 就自动动”,而是必须等图片资源真正就绪后,再触发 opacity 变化。直接在 DOMContentLoaded 时加类,或一上来就设 style.opacity = '0' 再切 '1',大概率会淡入一个空白框、抖动、甚至完全没动画。
为什么 onload + class 切换是最稳的起点
浏览器对 img 元素的 load 事件支持极好,且它明确表示:这张图的二进制数据已下载完成,并被解码为可绘制状态(现代浏览器中,load 已隐含基础解码就绪)。这是比 DOMContentLoaded 或定时器更可靠的“可淡入”信号。
-
img.complete属性可判断是否已在 JS 执行前加载完毕(如内联 HTML 中的图片),需单独处理,否则会跳过动画 - 必须监听
error事件——404 或跨域失败时,也要加loaded类,否则图片永远透明卡住 - transition 规则必须写在初始类里(如
.fade-in-img),不能只写在目标类(如.loaded)中,否则首次变化无过渡 - 避免用
display: none初始隐藏:它会破坏文档流,淡入时可能突然撑开布局
必须设宽高或 aspect-ratio,否则淡入就是抖动
没有尺寸约束的 <img> 在加载前是“无尺寸”的,即使 opacity 是 0,它仍会从 0 高度突然跳到真实高度,造成 layout shift(布局抖动)。这不是动画问题,是渲染流程缺陷。
- 最优解:HTML 中直接写
width和height属性(浏览器可据此预留空间) - 次优但更灵活:CSS 中用
aspect-ratio: 16 / 9(现代浏览器支持良好,2026 年已覆盖主流环境) - 容器加
overflow: hidden,防止图片加载瞬间溢出干扰其他元素 - 若图片来自 CMS 或无法预知比例,至少用 JS 读取自然宽高后动态设置 style,但不如声明式可靠
decode() 是更精准的“真正就绪”信号,但得有降级
img.decode() 返回 Promise,真正代表“图片像素已解码、可立即绘制”,比 load 更进一步。但它不支持所有环境(如某些旧版 Safari、CORS 图片未配头时会 reject),不能裸用。
- 先设
opacity: 0和transition,再赋值src,最后调decode()——顺序错一步,动画就失效 - Promise
catch中必须也加loaded类,否则失败即卡死 - 降级逻辑要明确:当
!('decode' in HTMLImageElement.prototype)时,退回到load/error监听 - 注意:
decode()不会触发load事件,两者互斥,不要混用同一张图
懒加载场景下,IntersectionObserver 是必要补充
长页面中,首屏外的图片不该在页面加载时就发起请求,更不该提前淡入。此时仅靠 load 不够——图还没开始加载,load 永远不会来。
- 用
IntersectionObserver监听进入视口(建议rootMargin: '0px 0px 100px 0px'提前触发) - 进入视口后,再设置
src(或data-src),并立刻绑定load/error或decode() - 不要省略
load监听:IntersectionObserver只管“可见”,不管“是否加载完成” - 原生
loading="lazy"可配合使用,但不能替代 JS 控制淡入时机
最易被忽略的点是:淡入动画本身不难,难的是让浏览器在正确的时间点、以正确的渲染上下文执行它。宽高缺失、字体未就绪、decode() 被忽略、load 未兜底 error —— 这些细节任何一个没处理,都会让淡入变成闪烁、跳变或静默失败。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











