可通过CSS过渡+JS监听load/error事件实现图片加载前占位动画,需设固定宽高防塌陷,用opacity过渡而非display:none,并注意lazy loading、decoding异步解码及布局稳定性问题。
HTML5 本身不提供内置的图片加载动画机制,但可以通过 img 元素的 loading 属性配合 CSS 过渡 + JavaScript 监听 load 和 error 事件来实现“等待中显示动画,加载完淡出”的效果

img 设置固定宽高或最小尺寸(否则动画容器会塌陷)opacity + transition 实现淡入,避免用 display: none —— 否则无法触发 load 事件opacity: 0.3 并加旋转/脉冲动画,加载成功后移除类名或设 opacity: 1
img 的 loading="lazy" 使用注意事项启用懒加载后,load 事件可能延迟触发,甚至在用户滚动到视口前不会触发。若你依赖该事件启动动画结束逻辑,就会出现“图片已显示但动画没停”的问题。
loading="lazy" 在 Safari 早期版本和部分安卓 WebView 中不支持,需降级 fallback(比如默认不 lazy,通过 JS 检测后动态添加)img.onload;建议同时监听 img.complete && img.naturalWidth > 0(同步加载完成时)IntersectionObserver:进入视口时先加 loading 动画类,再尝试设置 src 或触发 decode()
img 的 decoding="async" 影响动画时机吗?有影响,但常被忽略。decoding="async" 让浏览器异步解码图片,可能造成 load 事件触发后图像仍短暂空白或闪烁——此时如果动画已结束,用户会看到“先闪一下再稳定”的异常。
"sync",对多数小图够用;大图才考虑 "async"
decoding="async",动画收尾不应只依赖 load,最好加上 img.decode().then(...) 确保渲染就绪decode() 是 Promise,需处理 rejected 情况(如损坏图片),否则动画卡在 loading 状态img.addEventListener('load', () => {
img.decode().then(() => {
img.classList.remove('is-loading');
}).catch(() => {
img.classList.add('is-error');
});
});
background-image + @property 做渐进动画如果你能接受用 div 替代 img 标签(比如响应式背景图场景),CSS 新特性 @property 可以实现更可控的加载过渡,无需 JS。
@property --loaded 并设 initial-value: 0,然后用 transition 驱动 opacity 或 transform:has(img:loaded)(目前仅 Safari 16.4+ 支持)可实现自动检测,但兼容性差,生产环境慎用el.style.setProperty('--loaded', '1'),再由 CSS 响应div {
background-image: url('photo.jpg');
opacity: 0;
transition: opacity 0.3s ease;
}
div[style*="--loaded: 1"] {
opacity: 1;
}
实际中最容易漏掉的是:**没有给图片容器设 min-height 或 aspect-ratio,导致加载前高度塌陷、布局跳动,动画看起来像“突然弹出来”而不是平滑过渡**。