requestAnimationFrame、getComputedStyle未按预期工作问题
《In The Loop》事件循环演示动画代码失效问题排查
在实操Event Loop主题演讲《In The Loop》中的方块位移动画案例时,两种演示给出的实现方案均未出现「先移动到1000px位置、再动画返回500px位置」的预期效果,方块直接渲染到500px位置,测试环境为Ubuntu 22.04系统下Chrome 102.0.5005.61版本。
原演示给出的两种实现代码
方案1:嵌套requestAnimationFrame实现
button.addEventListener("click", () => { box.style.transform = "translateX(1000px)"; box.style.transition = "transform 1s ease-in-out"; requestAnimationFrame(() => { requestAnimationFrame(() => { box.style.transform = "translateX(500px)"; }); }); });
方案2:getComputedStyle强制重排的取巧方案
button.addEventListener("click", () => { box.style.transform = "translateX(1000px)"; box.style.transition = "transform 1s ease-in-out"; getComputedStyle(box).transform; box.style.transform = "translateX(500px)"; });
问题成因
既不是代码抄写错误,也不是事件循环核心规则发生变动,问题来自两个容易忽略的点:
- 缺少前置初始样式:两个方案生效的核心前提是,运动方块必须有明确的初始transform基准值(最常用的是
translateX(0)),且初始状态下不能提前挂载transition属性。如果没有设置初始transform基准值,浏览器会把同轮执行上下文中的两次transform修改合并到同一次样式计算流程中,直接取最后一个值也就是500px渲染,自然看不到先到1000px的过程;如果提前给方块加了全局transition规则,第一次修改transform到1000px时就会直接触发从初始位置到1000px的过渡,和后续逻辑冲突。 - 高版本Chrome渲染优化影响第二种hack方案:第一种嵌套
requestAnimationFrame的方案只要补全初始样式,在Chrome 102版本下可以直接正常运行,不存在兼容性问题。第二种getComputedStyle的方案失效是因为Chromium内核在2020年后做了渲染流水线优化:仅读取计算样式已经不足以强制浏览器提交当前的样式变更,浏览器会默认合并后续同属性的样式修改,不会触发实际的重排提交。要让这个老方案在新版本Chrome里生效,读完计算样式后加一行无视觉影响的样式写入打断合并即可,比如添加一句box.style.opacity = getComputedStyle(box).opacity;,强制浏览器把当前的transform值提交到渲染树,之后再修改到500px就能正常触发过渡。
实测验证:给方块补全基础样式
width: 50px; height: 50px; background: #f00; transform: translateX(0);后,第一种嵌套rAF的方案在Chrome 102版本下运行完全符合预期,不需要额外修改。
内容的提问来源于stack exchange,提问作者Michael K
相关产品推荐
相关产品推荐

