结论:只写 opacity 不会动,必须在默认状态声明 transition: opacity,且不能和 display: none 同时用;常见失效原因包括 transition 位置错误、display: none 干扰、JS 同步操作过快、overflow 裁剪、transition: all 误触发及其他属性干扰。

直接说结论:只写 opacity 不会动,必须在默认状态声明 transition: opacity,且不能和 display: none 同时用。
为什么加了 transition 和 opacity 却没动画?
最常见原因是过渡声明位置错了——transition 必须写在「起始态」(比如未 hover、未加 .show 类时)的规则里,而不是只写在目标态(如 .show)中。
其他典型失效场景:
-
display: none和opacity: 0混用:一旦display为none,元素脱离渲染树,transition完全不触发 - JS 同步移除 class 后立刻操作 DOM(比如紧接着调用
removeChild),动画根本没机会开始 - 父容器设了
overflow: hidden且子元素opacity: 0时发生裁剪,视觉上像“消失”,实则动画被截断 - 用
transition: all 0.3s代替显式声明opacity,结果其他属性(如background-color)意外参与过渡,干扰预期
如何用 JS 安全触发动画(淡入 → 淡出 → 移除)?
关键不是“加 class”或“删 class”,而是控制时机。直接删 class 后立即销毁节点,淡出会跳变。
立即学习“前端免费学习笔记(深入)”;
正确做法是监听 transitionend 事件,等动画真正结束再清理:
const el = document.querySelector('.fade-box');
el.classList.add('show'); // 淡入
// 淡出并移除
function fadeOutAndRemove() {
el.classList.remove('show');
el.addEventListener('transitionend', () => {
if (el.style.opacity === '0') el.remove();
}, { once: true });
}
注意点:
- 监听前确保元素当前有
transition声明,否则transitionend永远不会触发 - 加
{ once: true }避免重复绑定 - 检查
opacity值而非仅依赖类名,防止多次调用导致误删
visibility 和 opacity 要不要一起用?
要,但得小心配对方式。单独 opacity: 0 的元素仍占布局、可聚焦、能响应点击;而 visibility: hidden 保留空间但禁用交互——两者互补。
推荐组合写法:
.fade-box {
opacity: 0;
visibility: hidden;
transition: opacity 0.25s ease, visibility 0.25s step-end;
}
.fade-box.show {
opacity: 1;
visibility: visible;
}
重点说明:
-
step-end是关键:它让visibility在过渡结束瞬间才切换,避免中间态出现「可见但透明」或「不可见但可点」的错乱 - 别用
visibility 0.25s ease,缓动函数对visibility无意义,反而可能在 Safari 中引发渲染异常 - 如果需要彻底释放空间(比如折叠面板),改用
max-height+overflow: hidden,而非visibility
最容易被忽略的是:iOS Safari 对 opacity 过渡有合成层优化限制,尤其在 position: fixed 容器或滚动区域里。真遇到卡顿,加 will-change: opacity(仅限动画中元素),别全局滥用。


















