必须用 intersectionobserver 控制图片加载时机,lqip 触发 onload 不代表高清图就绪;需进视口即换 src、立即加 loaded 类、取消观察,并配合适配 safari 的 filter 过渡 css 与固定容器尺寸。

直接用 loading="lazy" 或只靠 img 的 onload 事件加 filter: blur() 过渡,90% 情况下会失效——不是代码写错,而是加载链路没对齐:LQIP 占位图一加载完就触发 load,高清图却还在网络队列里。
为什么 filter + transition 在 Safari 和首屏外图片上常不生效
根本问题不在 CSS 写法,而在渲染管线被干扰或时机错配:
-
filter: blur()在 iOS Safari 中必须配合display: block,且父容器不能有transform: translateZ(0)或will-change: transform,否则整个滤镜过渡被降级为静态快照 - LQIP(低质量占位图)是真实图片,它加载完成就会触发
img.onload,但此时data-src指向的高清图可能连 DNS 查询都没开始 - 所有
img一挂载就监听load,滚动时大量未进视口的图片提前请求、解码、触发动画,CPU 突增,动画卡顿甚至跳帧
必须用 IntersectionObserver 控制加载开关
这不是“锦上添花”,而是唯一能精准锚定“用户即将看到这张图”的时机。关键动作是:等进视口 → 立即换高清图 → 立即加 loaded 类 → 立即取消观察。
- 创建 observer 时设
rootMargin: "50px",让图片在进入视口前 50px 就开始加载,避免滑动过快导致白屏 - 回调中先执行
img.src = img.dataset.src,**立刻**加loaded类,**不要等img.onload** —— 高清图替换后浏览器会自动触发重绘,CSS 过渡自然开始 - 紧接着调用
observer.unobserve(img),防止重复加载;若需复用 observer,可改用threshold: [0.05](5% 进入即触发) - 确保每个
<img class="blur-up">都被observer.observe(img)绑定,DOM 渲染完再初始化 observer 会导致首屏图片漏绑
CSS 必须显式拆开 transition 并固定容器尺寸
模糊渐变不是靠 JS 动画驱动,而是靠浏览器原生合成器做滤镜+透明度双通道过渡。任何布局抖动都会打断它。
- 必须写成:
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);不能简写为transition: all或只写filter - 初始状态设
filter: blur(6px); opacity: 1;,完成类里写filter: blur(0); opacity: 1;;避免用blur(10px)以上,iOS WebKit 会降级为均值模糊且不触发transitionend - 容器必须有明确宽高或
aspect-ratio,图片用object-fit: cover;否则 LQIP 和高清图尺寸不一致会引发 layout shift,过渡看起来像“抽搐” - 如果用双图叠加(缩略图底图 + 高清图绝对定位覆盖),底图要加
background: #f5f5f5防 PNG 透明穿透,且两张图都设width: 100%; height: 100%
真正卡住人的从来不是 blur() 参数调不对,而是没意识到:LQIP 是占位信号,不是加载完成信号;IntersectionObserver 是开关,不是装饰;而 opacity 和 filter 的过渡能否跑起来,取决于你有没有把它们从 transform 干扰、布局抖动、错误加载时机这三重陷阱里彻底解耦出来。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











