intersectionobserver 是唯一推荐的滚动触发方案,需设 threshold: [0.1]、监听 isintersecting === true 时执行动画、用 transform+opacity 实现 gpu 加速过渡,并在回调中 unobserve() 避免冗余计算。

滚动监听 + IntersectionObserver 是唯一靠谱的触发方式
用 window.onscroll 或 scroll 事件手动判断元素位置,性能差、抖动严重、容易漏触发。现代浏览器下,IntersectionObserver 是唯一推荐方案——它原生支持懒加载式监听,不阻塞主线程,且能精准捕捉“刚进入视口”的瞬间。
常见错误是监听后立刻加 transition 类,但元素还没渲染完成,导致过渡失效;或在回调里反复添加/移除类,没做防重复处理。
- 必须等
isIntersecting === true才执行动画逻辑,intersectionRatio > 0不够可靠(可能只是像素级擦边) - 观察器初始化时设
threshold: [0],但真正想触发滑入,建议用[0.1]避免过早触发 - 每个目标元素应单独实例化 observer,或确保回调中通过
entry.target精准操作对应 DOM 节点
transform + opacity 是最稳的滑入组合
用 margin-top 或 top 触发过渡会引发重排,卡顿明显;display: none 切换则完全无法过渡。只有 transform 和 opacity 属于合成属性,走 GPU 加速,帧率稳定。
典型写法是初始状态设 transform: translateY(20px) + opacity: 0,进入视口后加类切换为 transform: translateY(0) + opacity: 1,并配 transition: transform 0.4s ease-out, opacity 0.4s ease-out。
立即学习“前端免费学习笔记(深入)”;
- 不要只写
transition: all 0.4s—— 容易意外触发其他属性过渡,比如height改变引发布局抖动 - 如果元素本身有
will-change: transform,可提前提示浏览器优化,但别滥用,否则内存开销上升 - 移动端 Safari 对
transform: translateY()的亚像素渲染有时不准,可补transform: translateZ(0)强制硬件加速
滚动中频繁触发?得靠 unobserve() 控制生命周期
默认情况下,元素一旦进入视口,observer 会持续报告其状态,后续滚动中反复回调,造成冗余计算。多数场景下,滑入一次就够了,没必要监听退出或反复进出。
最简做法是在回调里立刻调用 observer.unobserve(entry.target),让该元素“监听一次即销毁”。这是性能关键点,也是新手最容易忽略的。
- 如果需要“进/出”都响应(比如悬停态恢复),才保留观察,但务必加
if (entry.isIntersecting)分支判断 - 别在
unobserve()后还试图对entry.target做 DOM 操作——节点可能已被移除或隐藏,先检查entry.target.isConnected - 页面动态插入新元素时,要重新调用
observer.observe(el),不能指望旧 observer 自动接管
CSS 过渡失效?先查这三件事
写了 transition 却没动画,90% 是以下三个原因:初始状态没声明、类名没正确切换、或者 CSS 优先级被覆盖。
示例:如果元素默认是 opacity: 1,但你只在 .in 类里写了 opacity: 1,那它根本不会“过渡”,因为起始值和结束值一样。
- 初始样式必须显式写出过渡前状态,比如
.slide-in { opacity: 0; transform: translateY(20px); transition: opacity 0.4s, transform 0.4s; } - JS 中用
element.classList.add('in'),而不是element.className = 'in'—— 后者会清空所有已有 class - 检查 DevTools 的 Computed 面板,确认
transition属性确实生效,且没有被!important或更高权重规则屏蔽
复杂点在于:滚动速度极快时,有些元素可能“跳过”了 isIntersecting === true 状态,尤其在低帧率设备上。这时候得靠 threshold 调整或 fallback 到定时检测,但那就不是纯 CSS 过渡能解决的事了。









