最小可行写法需同时控制 opacity 和 visibility;仅用 opacity 会卡住,因未触发重排;配合 visibility: hidden 可完成淡入淡出,且避免 display 不支持 transition 的问题。

transition + opacity 实现页面淡入淡出的最小可行写法
直接生效的关键是:必须同时控制 opacity 和 visibility(或 display),只靠 transition: opacity 会卡住、无法触发重排,导致过渡不完整。
-
opacity: 0只让元素透明,但依然占据布局空间、仍可响应事件 - 想真正“退出”页面,得配合
visibility: hidden(保留占位)或display: none(完全移除) - 但
display不支持 transition,所以标准做法是:用opacity过渡,再用visibility控制可交互性
示例(单页路由切换场景):
.page {
opacity: 1;
visibility: visible;
transition: opacity 0.3s ease, visibility 0.3s ease;
}
.page.is-exiting {
opacity: 0;
visibility: hidden;
}
移动端 touch 触发后 transition 失效的常见原因
不是 CSS 写错了,而是浏览器在 touch 事件中没及时触发重绘——尤其在快速连续切换时,opacity 值变了,但 transition 没被“看见”。
- 典型现象:点击后页面瞬间消失,无淡出效果;或首次有效、后续失效
- 根本原因是:CSS 属性变更和重绘时机不同步,缺少强制 reflow 的钩子
- 最轻量解法:在设置
is-exiting类前,强制读取一次offsetHeight,触发同步 layout - 别用
setTimeout(..., 0)模拟,它不可靠,且增加延迟
JS 侧建议写法:
立即学习“前端免费学习笔记(深入)”;
el.classList.remove('is-active');
el.offsetHeight; // 强制触发 layout
el.classList.add('is-exiting');
transition-duration 在 iOS Safari 上的兼容性陷阱
iOS 15.4+ 开始,transition 对 opacity 的动画帧率明显下降,尤其在低功耗模式或后台标签页中,容易卡成两帧。
- 问题不在代码,而在 WebKit 渲染策略:它会对非 viewport 内、非活跃 tab 的 transition 自动降级
- 如果你的页面有“滑动返回”手势(iOS 原生),
opacity过渡可能被系统接管,导致自定义 transition 被忽略 - 稳妥方案:加
will-change: opacity提前告知浏览器该属性将变,但仅对当前活跃元素使用,避免过度触发 GPU - 不要给整个
.page都加will-change,内存开销大,反而拖慢首屏
opacity 过渡与可访问性的隐性冲突
屏幕阅读器(如 VoiceOver)不会因为 opacity: 0 就跳过元素,只要它还在 DOM 里、visibility: visible 或没设 aria-hidden,就会继续朗读。
- 后果:用户听到“上一页内容”还在播报,但页面已显示新内容,体验割裂
- 正确做法:过渡开始时同步设置
aria-hidden="true",过渡结束后清理 - 别依赖
display: none来规避——它虽能隐藏,但若过渡中突然移除,会导致读屏器中断或报错 - 更安全的是用
aria-hidden+opacity+visibility三者联动
过渡结束回调推荐用 el.addEventListener('transitionend', handler, { once: true }),而不是靠定时器猜时间。
opacity 过渡看着简单,但真正在移动端跑稳,得同时踩准渲染时机、系统干预点、辅助技术边界——漏掉任意一环,用户看到的就不是“优雅”,而是“卡顿”或“错乱”。


















