图片淡入关键在于等像素真正就绪后再启动动画,须监听img.onload或img.decode()(需降级),配合opacity过渡、预设宽高/aspect-ratio、complete检查及error兜底,禁用display:none和setTimeout。

图片懒加载后平滑淡入,关键不是“等页面加载完再动”,而是“等图片像素真正就绪后再启动动画”。直接给 <img loading="lazy"> 加 opacity: 0 和 transition 会闪白或跳帧——因为动画提前播了,图还没解码好。
必须监听真实就绪信号,不能只靠 loading="lazy"
loading="lazy" 只控制请求发起时机,不表示图片已可绘制。真正可靠的信号有两个:
- img.onload:兼容性最好,表示资源下载完成且基础解码就绪(现代浏览器中已隐含可渲染)
- img.decode():更精准,代表像素已解码、可立即绘制;但需降级处理(旧 Safari 或 CORS 图片可能 reject)
别用 setTimeout 模拟延迟——网络一慢就失效,用户滚动快时动画甚至没机会播。
初始状态和 CSS 过渡要写对
淡入动画失效,80% 出在 CSS 写法上:
立即学习“前端免费学习笔记(深入)”;
- 过渡规则(
transition: opacity 0.4s ease)必须写在初始类里(如.lazy-img),不能只写在目标类(如.loaded)中 - 初始状态设
opacity: 0,但不能用display: none或visibility: hidden,否则 transition 不触发 - 必须预设宽高:
width/height属性(最稳)或aspect-ratio(推荐,2026 年已全覆盖);否则图片加载瞬间高度突变,造成布局抖动
JS 控制逻辑要兜底完整
懒加载图片的 JS 处理不能只盯 load:
- 先查
img.complete === true:内联图或缓存图可能 JS 执行前就加载好了,不检查就会跳过动画 - 未完成的,监听
load和error两个事件:404 或跨域失败时也得加.loaded类,否则图片永远透明卡住 - 若用
decode(),catch中同样要加类,并退回到load监听(decode()和load互斥,不能混用)
搭配 IntersectionObserver 更自然
纯靠 load 会一加载完就淡入,但用户可能还没看到它。结合视口监听更符合体验逻辑:
- 用
IntersectionObserver监听图片进入视口(threshold: 0.1表示 10% 进入即触发) - 回调中再检查
img.complete,已完成则立刻加类;未完成则绑定load/error - 触发后调用
observer.unobserve(img),避免重复执行



















