opacity过渡需严格分离动画与dom操作:先设opacity:0触发动画,再于transitionend回调中处理visibility/display等离散属性,避免display/visibility中断动画或未声明初始opacity导致瞬切。

直接用 transition 配 opacity 就能做出平滑显隐,但绝大多数人卡在“动画没动”或“啪一下消失”——问题几乎都出在动了 display 或 visibility,而不是只改 opacity。
为什么 opacity transition 一隐藏就“瞬切”?
根本不是写法错,是浏览器看到 display: none 或 visibility: hidden 就立刻终止所有正在运行的 opacity 动画帧。哪怕你先设 opacity: 0,再设 display: none,只要 JS 是同步执行的(比如两行赋值),浏览器仍会丢弃过渡过程。
-
display是离散值,无法插值,transition对它完全无效 -
visibility: hidden虽然保留占位,但它本身不可动画;一旦生效,会冻结当前opacity值(哪怕还没到 0) - 初始状态没显式写
opacity: 1(靠继承或默认值),浏览器无法确定“从哪开始过渡”
正确触发流程:只改 opacity,动画完再收尾
视觉变化和 DOM 占位/交互控制必须严格分两步,且按时间顺序执行。
- 默认状态必须声明:
opacity: 1; transition: opacity 0.25s ease-out; - 触发隐藏时,仅设置
opacity: 0(通过 class 切换或el.style.opacity = '0') - 监听
transitionend事件,在回调里判断event.propertyName === 'opacity'后,再设visibility: hidden或pointer-events: none;如需脱离文档流,此时再加display: none - 若要淡入同时禁用点击(比如模态框遮罩),可配
transition: opacity 0.3s ease, pointer-events 0s 0.3s,让pointer-events延迟生效
SCSS 封装与多属性过渡注意事项
用 @mixin 封装能避免重复写时长和缓动函数,但要注意多属性过渡不能偷懒写 transition: all。
- 推荐写法:
transition: opacity 0.25s cubic-bezier(0.4, 0, 0.2, 1), transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);—— 显式列出,各属性独立控制 -
transition: all 0.25s会把background-color、height等意外变化也套上动画,导致行为不可控 - 移动端 Safari 渲染敏感,必要时加
will-change: opacity提前提示,但别滥用(只在真正需要动画的元素上加) - 轮播或密集列表中,避免大量元素同时跑
opacity动画,可能卡顿;可考虑用transform: translateZ(0)强制合成层
最容易被忽略的是:动画结束后的清理动作必须等 transitionend 回调触发,且校验 propertyName。很多人监听了事件却没判断属性名,结果 transform 或 color 的变化也误触发了 display: none,造成显隐逻辑错乱。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











