必须用 intersectionobserver + data-src 替换高清图,而非依赖 img load 事件;因 lqip 占位图会误触发 load,且 safari 中 filter 过渡需 display: block 并避免 transform 干扰。

filter: blur() + opacity 做渐显,必须配合 JS 控制类切换时机,且不能依赖图片 load 事件做初始状态判断——因为 LQIP 占位图本身也会触发 load,容易误判“已加载完成”。
为什么不能只靠 CSS transition 和 onload?
很多人写完 img { filter: blur(6px); opacity: 1; transition: filter 0.4s, opacity 0.3s; } 就以为完事了,结果发现:图片一闪就清晰,或根本没过渡。问题出在两点:
- 浏览器对
img元素的load事件触发时机不统一——LQIP 图一加载完就触发,但此时高清图还没开始下载 -
filter过渡在某些 Safari 版本中不生效,除非元素有明确display(比如display: block)且不在 document fragment 中提前渲染 - 没处理视口外图片:滚动时批量触发
load,CPU 占用飙升,动画卡顿
正确做法:用 IntersectionObserver + data-src 替换
核心是“等它进视口,再发起高清图请求”,不是等 DOM 加载完就急着替换。
- HTML 中
<img src="photo-lqip.jpg" class="blur-up">,src指向小图,data-src存高清图地址 - CSS 必须写全:
.blur-up { display: block; filter: blur(6px); opacity: 1; transition: filter 0.4s cubic-bezier(0.22, 0.61, 0.36, 1), opacity 0.4s cubic-bezier(0.22, 0.61, 0.36, 1); }+.blur-up.loaded { filter: blur(0); opacity: 1; } - JS 创建
IntersectionObserver时设rootMargin: "50px",让图片提前 50px 开始加载 - 回调里执行
img.src = img.dataset.src,然后立即img.classList.add("loaded"),再调用observer.unobserve(img)
常见错误与绕过方案
实际部署时最常踩的坑不是语法,而是资源加载链路没理清:
-
data-src地址 404 或跨域:检查 Network 面板,确认高清图返回Content-Type: image/*且无 CORS 拦截 - 模糊过渡在 iOS Safari 上失效:确保父容器没设
transform: translateZ(0)或will-change: transform,这两者会干扰filter渲染管线 - 首屏图片不触发 observer:检查是否忘了对每个
<img class="blur-up">调用observer.observe(img);也可能是页面初始化太晚,DOM 已渲染完毕但 observer 还没挂载 - 想兼容 IE?别费劲了。
filter: blur()在 IE 完全不可用,连-ms-filter的Blur也只支持固定像素、无过渡,现代项目应直接放弃
Cache-Control: public, max-age=31536000,LQIP 图反复请求……这些细节不卡在具体某一行代码上,但卡在整个流程里。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











