最轻量解法是body初始opacity:0并设transition,DOMContentLoaded时加loaded类实现淡入;须避免display:none导致重排,用opacity+visibility组合;配合font-display:swap防字体闪白;Safari需用包裹div替代body。

页面切换时加淡入效果,不是加个 fadeIn 就完事——浏览器根本不会等你“慢慢变出来”,它只按渲染流水线走。关键在于控制样式计算的时机和层叠上下文,否则动画会直接跳帧、卡死、或完全不触发。
transition: opacity 0.3s 为什么写了也不动
最常见的情况是:JS 里连续两行改 style.opacity,比如 el.style.opacity = '0'; el.style.opacity = '1';。浏览器把这当成一次样式批量更新,中间没有重排(reflow)触发点,过渡直接被跳过。
- 必须让浏览器“意识到”这是两个独立帧:在两句之间插一句强制读取布局的语句,比如
el.offsetHeight或getComputedStyle(el).opacity - 更稳妥的做法是操作 class:
el.classList.remove('show');→el.offsetHeight;→el.classList.add('show'); - 别用
display: none做初始隐藏——它会让元素彻底退出布局流,opacity过渡无从谈起;该用visibility: hidden配合opacity: 0
CSS 中 transition 声明写全了没
transition 属性漏写任何一环,动画就失效。不是“写了 transition 就自动动”,而是要精确告诉浏览器“哪个属性、花多久、怎么动”。
- 不能只写
transition: all 0.3s;——可能意外触发动画其他属性(比如height或margin),造成布局抖动 - 必须明确限定:比如
transition: opacity 0.3s ease-in-out, visibility 0.3s;(注意visibility本身不可过渡,但配合opacity可避免点击穿透) -
opacity初值必须在默认状态(非:hover、非.active)里声明,比如.page { opacity: 0; },否则过渡起点不存在
页面级切换时,旧内容怎么安全淡出
新内容淡入的同时,旧内容得淡出,但不能简单 remove()——那样会中断动画,甚至导致新内容闪一下再出现。
立即学习“前端免费学习笔记(深入)”;
- 监听
transitionend事件,只在opacity动画结束且propertyName === 'opacity'时才移除旧 DOM 或设display: none - 确保新旧容器处于同一层叠上下文(比如都设
position: absolute或同级z-index),否则可能出现“新内容从旧内容下方钻出来”的错层 - 服务端渲染场景下,直接在 HTML 模板里输出
<main class="fade-in is-loaded"></main>,跳过 JS 触发步骤,避免首屏白屏或闪烁
jQuery 的 fadeIn() 看似简单,但有隐含前提
它内部做了兼容处理,但前提是元素初始状态必须是“可动画但不可见”——不是靠 opacity: 0,而是靠 display: none。这意味着:
- 如果元素原本是
display: block+opacity: 0,fadeIn()会先设display: block(无效),再渐变opacity(有效),但视觉上仍是“突然出现+渐变”,失去意义 - 它默认不处理
visibility,所以透明但占位、可点击的问题依然存在,需额外 CSS 修补 - 若要链式控制(比如淡出后加载新内容再淡入),必须用回调函数:
$old.fadeOut(300, () => { loadNew(); $new.fadeIn(300); });
真正难的不是写对一行 @keyframes,而是让每个过渡都在正确的时间点、正确的渲染帧里被浏览器捕获。0.3s 是舒适区间,但比时间更重要的是:是否强制触发了重排,是否锁定了层叠上下文,以及是否在动画结束后干净地收尾——比如把 visibility: hidden 留给下一次复用,而不是靠 display: none 彻底丢弃状态。



















