先记住这个答案
CSS transition 比较前后样式中指定属性的值。opacity、transform 等值可以逐帧插值;display 从 none 到 block 没有中间值。默认情况下它会直接切换,但设置 transition-behavior: allow-discrete 后可以把切换安排到过渡边界;入场还需 @starting-style 为透明度等属性提供起点。
- 计算值改变且属性列入过渡才会触发
display不插值,可在允许离散过渡时切换- 类名切换和伪类都能触发过渡
opacity配合visibility可做渐隐
什么叫「可插值的属性」
浏览器做过渡的本质是在起始值和目标值之间逐帧计算中间值。opacity 从 1 到 0,中间可以是 0.7、0.3,每一帧都有合理的视觉表现。color 从红到蓝,中间可以混出紫色。这些属性的值是连续的,所以能插值。
.box {
opacity: 1;
transform: scale(1);
/* 声明要过渡的属性 */
transition: opacity 0.3s ease, transform 0.3s ease;
}
.box:hover {
opacity: 0.6;
transform: scale(1.05);
}鼠标移入时,opacity 的计算值从 1 变成 0.6,transform 的计算值也变了,两个属性都在 transition 列表里,所以同时平滑过渡。移出时计算值变回去,也会触发反向过渡。
display: none 为什么默认直接切换
display: none 时元素不生成盒子;改为 block 的那一刻,浏览器默认没有可供过渡使用的前一帧样式,所以单写 transition: opacity .2s 通常不会出现淡入。@starting-style 可以明确给入场的透明度起点。
退场时也不能只让 opacity 变零并立即切到 display: none。可把 display 放进过渡列表并启用 allow-discrete,让隐藏切换发生在透明度过渡结束时;目标浏览器不支持时可用 visibility 方案。
opacity 加 visibility 替代CSS.tooltip {
opacity: 0;
visibility: hidden;
transition: opacity 0.2s ease, visibility 0s 0.2s;
}
.trigger:hover .tooltip {
opacity: 1;
visibility: visible;
transition: opacity 0.2s ease, visibility 0s;
}显示时 visibility 立刻变成 visible(延迟 0s),opacity 从 0 过渡到 1。隐藏时 opacity 先花 0.2 秒渐隐,然后 visibility 延迟 0.2 秒切换成 hidden,这时候元素才真正不可点击。
哪些操作会导致计算值变化
加类名、移除类名、:hover、:focus、:checked 这些伪类切换,以及 JavaScript 直接修改 element.style 或通过 classList 操作,都会让浏览器重新计算样式。只要计算结果和之前不同,对应属性就会触发过渡。
有个细节:如果用 JavaScript 在同一帧里先设值再改值,浏览器可能会合并这两次操作,只用最终值参与过渡。想强制触发中间态,需要在两次赋值之间读一次 getComputedStyle,迫使浏览器刷新计算。
容易答错的地方
- 以为
transition: all能让所有属性都过渡 transition: all扩大了候选属性,但不意味着每个属性都平滑插值。display需要允许离散过渡,font-family仍会直接切换。而且all容易引发意外过渡,比如改个背景色也会慢慢变,实际项目里建议明确列出要过渡的属性。- 以为 JS 改了 style 就一定会触发过渡
- 同一帧内连续修改 style,浏览器只用最后的值。比如先设
opacity = 0,紧接着设opacity = 1,浏览器跳过中间态,直接渲染 1,没有过渡。要先设初始值,读一次getComputedStyle(el).opacity强制刷新,再设目标值。
面试官还会怎么问?
transition-property 设成 none 会怎样?
所有属性的值变化都不会产生过渡,相当于关掉了 transition。这在需要瞬间切换样式(比如拖拽重置位置)时很有用。
新版浏览器能让 display 过渡了吗?
可以。用 transition-behavior: allow-discrete 把 display 放进离散过渡;从 none 进入显示状态时,再用 @starting-style 给 opacity 等属性提供起始值。display 本身不会逐帧变成中间形态:入场开始时切到可见,退场结束时切到 none。实际项目要按目标浏览器检查兼容性。
过渡被打断会怎样?
如果过渡进行到一半时属性值又变了,浏览器会从当前的中间值开始新的过渡,而不是跳回起点。这就是为什么快速移入移出时动画看起来很顺滑,而不是「闪回原位再重新开始」。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。