滚动浮现动画需用 IntersectionObserver 触发 CSS 类添加,@keyframes 定义 opacity 0→1 和 translateY(20px)→0,配合 forwards 保持终态,避免内联样式覆盖、动画名错误或父容器 overflow 隐藏导致失效。

用 @keyframes 控制 opacity 和 translateY 实现滚动浮现
滚动时文字逐渐浮现,本质不是“自动触发”,而是结合 IntersectionObserver 或滚动监听 + CSS 动画类切换。纯 CSS 无法感知滚动位置,必须靠 JS 判断元素是否进入视口,再添加动画类。
关键点:动画本身用 @keyframes 定义,但「何时播放」由 JS 控制。否则所有元素一加载就动,或完全不动。
-
opacity从 0 → 1 控制透明度渐显 -
transform: translateY()从 20px → 0 避免文字突兀跳入(比translateY(0)更自然) - 动画需设
forwards,否则结束后会回退到初始状态
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fade-in-up {
opacity: 0;
transform: translateY(20px);
animation: fadeInUp 0.6s ease-out forwards;
}
用 IntersectionObserver 触发动画类添加
比 window.addEventListener('scroll') 性能更好,避免频繁重排重绘。只在元素进入视口时加类,且可设 threshold 控制触发时机(如 0.1 表示 10% 进入即触发)。
- 每个目标元素只监听一次,首次进入后可调用
unobserve()防止重复执行 - 若需「反复进出都动画」,去掉
unobserve(),但要加判断避免重复添加类 - 注意:Safari 旧版本需 polyfill,
IntersectionObserver在 iOS 12.2+ 原生支持
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('fade-in-up');
observer.unobserve(entry.target); // 只触发一次
}
});
}, { threshold: 0.1 });
document.querySelectorAll('.js-fade').forEach(el => observer.observe(el));
常见失效原因:CSS 动画不播放
即使 JS 正确添加了类,动画也可能不执行。最常踩的坑是:
立即学习“前端免费学习笔记(深入)”;
- 元素已有
opacity: 0和transform: translateY(20px)内联样式,覆盖了 class 中的初始值 → 移除内联样式,或用!important(不推荐),优先改结构 - 动画名拼错,比如写成
fadeInup而非fadeInUp→ 检查控制台是否有Invalid property value类警告 - 未设置
animation-fill-mode: forwards→ 动画播完立刻回退,看起来像没动 - 父容器有
overflow: hidden且元素初始位置在裁剪区外 → 确保translateY(20px)不被截断,或临时加margin-top: -20px协助定位
移动端适配与性能注意点
在 iOS Safari 或低端安卓机上,transform 和 opacity 是唯二能硬件加速的属性。其他如 height、margin 触发动画会卡顿。
- 务必用
transform: translateY(),不要用top或margin-top - 避免同时对大量元素启用 observer,可加防抖或分批 observe
- 若页面内容极长,考虑用
rootMargin扩大触发区域(如'100px'),让动画提前开始,减少“追着滚条跑”的感觉 - 测试真机:模拟器常掩盖渲染延迟,iOS 上尤其要注意
will-change: transform的滥用(仅在必要时加)
animation 是否生效——别只盯着元素面板里的 class。

