最健壮方案是intersection observer api+css动画:元素真正进入视口才触发动画,避免误触发、卡顿及ios safari滚动抖动问题;需先设初始类(如img-fade-in)并禁用active,再由observer监听添加active类执行过渡动画。

图片随页面向下滚动逐渐显现,本质是「进入视口时触发动画」,不是靠 scroll 事件手动算偏移,而是用 Intersection Observer API + CSS 动画组合实现——它比监听 window.scrollY 更精准、更轻量、且天然支持移动端。
用 Intersection Observer 触发滑入动画
这是目前最健壮的方案:元素真正进入视口才执行动画,避免空跑、误触发,也不受 iOS Safari 滚动抖动影响。
- 给图片加一个初始类,比如
class="img-fade-in",CSS 中设opacity: 0; transform: translateY(20px); - 用
IntersectionObserver监听该类元素,一旦isIntersecting === true,就添加active类 - CSS 中定义
.img-fade-in.active { opacity: 1; transform: translateY(0); transition: opacity 0.4s ease, transform 0.4s ease; } - 注意:不要在
observer.observe()前就给元素加active,否则首次加载会直接显示,失去“滚动显现”效果
为什么不用 window.addEventListener('scroll')
监听 scroll 容易出问题:iOS Safari 地址栏收放会重置 window.scrollY,导致图片位置跳变;滚动事件高频触发还可能卡顿;而且无法区分“图片是否真在视口里”,容易在未进入时就提前动画。
- 不推荐用
window.scrollY和getBoundingClientRect()手动判断,代码冗长且边界 case 多(比如父容器transform或overflow会影响top值) - 如果必须用 scroll,至少用
requestAnimationFrame节流,且每次都要重新计算el.getBoundingClientRect().top -
IntersectionObserver的threshold: 0.1可以让图片还有 10% 高度进入视口时就开始动画,体验更顺滑
CSS 动画参数怎么调才自然
动画时长和缓动函数直接影响感知质量。太慢显得迟钝,太快像闪现;线性过渡生硬,ease-out 或 cubic-bezier(0.25, 0.46, 0.45, 0.94) 更接近物理惯性。
- 推荐动画时长:0.4s–0.6s,
transition写全属性:transition: opacity 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94), transform 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) - 别只写
transition: all 0.5s,会把width、height等无关属性也纳入过渡,引发意外重排 - 务必加
forwards(如果是@keyframes)或确保active类含最终状态,否则动画结束瞬间会回退
真正容易被忽略的是 rootMargin 和懒加载协同:如果图片本身用了 loading="lazy",而 IntersectionObserver 又没等图片加载完成就触发动画,可能出现“先动画后加载”的白屏闪烁。稳妥做法是监听 img.onload 后再让 observer 开始工作,或者干脆关掉 loading="lazy" —— 对首屏以下图片,这点性能差异远小于视觉断裂感。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











