真正懒加载的核心是延迟设置src直到图片进入视口,而非仅靠opacity+transition实现视觉渐显;前者节省带宽提升首屏性能,后者图片早已下载完毕。

图片懒加载为什么不能只靠 opacity + transition
单纯用 opacity 和 transition 实现“渐显”不是真正的懒加载,它只是视觉动效——图片资源在页面加载时就已发出请求(src 已写死),浏览器会提前下载所有图片,完全没节省带宽或提升首屏性能。真正懒加载的核心是:**延迟设置 src(或 srcset)直到图片进入视口**,再配合透明度过渡实现淡入效果。
用 IntersectionObserver 触发 src 设置 + opacity 过渡
现代方案应结合 IntersectionObserver 检测可见性,避免频繁监听 scroll。关键点在于:初始用占位属性(如 data-src)存真实地址,进入视口后再赋值给 src,同时移除隐藏类触发 CSS 过渡。
- HTML 中用
<img class="lazy" alt="">,不设src - CSS 定义默认隐藏态和过渡:
.lazy { opacity: 0; transition: opacity 0.3s ease-in-out; } .lazy.loaded { opacity: 1; } - JS 监听并加载:
const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target; img.src = img.dataset.src; img.classList.add('loaded'); observer.unobserve(img); // 加载完即停止观察 } }); }); document.querySelectorAll('img.lazy').forEach(img => observer.observe(img));
为什么 transition 不生效?常见 CSS 坑
即使 JS 正确设置了 src 和 loaded 类,动画也可能卡住或跳变。原因常出在以下几点:
-
img元素默认是inline,而transition对inline元素的opacity虽然支持,但若父容器有overflow: hidden或图片未设定宽高,可能触发渲染异常 - 图片加载完成前,元素高度为 0,
opacity变化不可见;必须确保容器有占位(如aspect-ratio、padding-top百分比或固定height) - 如果使用
background-image替代<img alt="如何用css实现图片懒加载_结合opacity和transition在滚动时逐渐加载图片" >,需注意background-image不触发load事件,且opacity作用于整个元素,不是背景本身
兼容性与降级:没有 IntersectionObserver 怎么办
IE 完全不支持 IntersectionObserver,旧版 Safari 也需 polyfill。若必须兼容,可用 getBoundingClientRect() + scroll 事件模拟,但要注意节流和性能:
立即学习“前端免费学习笔记(深入)”;
- 用
throttle包裹 scroll 回调(如 Lodash 的throttle(fn, 100)) - 每次滚动检查
img.getBoundingClientRect().top 且 <code>top > 0(确保在视口内) - 加载后从待检查列表中移除该
img,避免重复执行 - 更稳妥的做法是引入官方 polyfill:
<script src="https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserver"></script>
真正起作用的是 src 的延迟设置时机,opacity 过渡只是锦上添花;漏掉占位、不处理加载失败、忽略宽高塌陷,都会让这个“渐显”看起来像 bug 而不是动效。










