如何用CSS过渡与JavaScript实现元素先瞬间放大再平滑缩小?
解决CSS过渡中“瞬间切换再平滑过渡”的重排问题
我有个span小部件,初始样式是这样的:
span.widget { line-height: 0; font-size: 150%; transition: font-size 1s; }
用JS把字体改成200%时,能平滑放大,效果没问题:
var widget = document.getElementById('the_widget'); widget.style.fontSize = '200%';
但我想实现先瞬间放大到200%,再平滑缩小回150%,试了下面的代码却完全没效果:
widget.style.transition = 'none'; widget.style.fontSize = '200%'; widget.style.transition = 'font-size 1s'; widget.style.fontSize = '150%';
原因很简单:浏览器会批量处理样式修改,第一次改完font-size后没触发重排,第二次修改直接被合并了,导致元素没任何变化。不光font-size,改height、width这类尺寸过渡时也会碰到同样的问题。
两种可行的解决方法:
方法1:用requestAnimationFrame延迟过渡设置
利用浏览器的渲染周期,把恢复过渡和缩小尺寸的操作放到下一帧执行,让浏览器先处理完无过渡的放大操作:
var widget = document.getElementById('the_widget'); // 先关闭过渡,瞬间放大 widget.style.transition = 'none'; widget.style.fontSize = '200%'; // 在下一渲染帧恢复过渡,触发平滑缩小 requestAnimationFrame(() => { widget.style.transition = 'font-size 1s'; widget.style.fontSize = '150%'; });
方法2:强制触发重排
通过读取元素的布局属性(比如offsetWidth),逼浏览器立即处理之前的样式修改并触发重排,这样后续的样式变化就能被正确识别:
var widget = document.getElementById('the_widget'); widget.style.transition = 'none'; widget.style.fontSize = '200%'; // 读取offsetWidth强制触发重排,不需要存变量,用void就行 void widget.offsetWidth; widget.style.transition = 'font-size 1s'; widget.style.fontSize = '150%';
原理补充
浏览器为了优化性能,会把同一JS执行上下文里的多个DOM样式修改攒到一起,只做一次重排/重绘。要么等下一渲染周期再执行后续操作,要么强制浏览器立即重排,就能把两次样式修改拆分开,实现想要的“瞬间放大+平滑缩小”效果。
内容的提问来源于stack exchange,提问作者cazort
相关产品推荐
相关产品推荐

