如何无布局卡顿地从top/left定位切换为transform等价过渡
问题描述
我有一个通过top和left属性定位的div,需要动态将其定位方式从top/left切换为等价的transform方式。但遇到的问题是:当设置top: 0、left: 0及对应的transform属性时,div会先瞬间跳到(0,0)位置,之后才会正确定位。我知道修改top和left会触发布局重绘。
首次执行Animation1时会出现该问题,但之后可正常工作。
HTML代码
<div class="red gpu" style="left: 120px; top: 100px;"></div> <div class="border original">120,100</div> <div class="border moved">270,180</div> <button onclick="animate1()">Animate1</button> <button onclick="animate2()">Animate2</button>
JS代码
function animate1() { let div = document.querySelector('div'); div.style.cssText = ` left: 0px; top: 0px; transform: translate(120px, 100px) translate(150px, 80px);`; } function animate2() { let div = document.querySelector('div'); div.style.left = `0px`; div.style.top = `0px`; div.style.transform = `translate(120px, 100px)`; }
CSS代码
div { width: 100px; height: 100px; } .red { background-color: red; transform-origin: 50% 50%; transition: transform 2s; position: absolute; } .border { border: 1px solid blue; position: fixed; } .original { left: 120px; top: 100px; } .moved { left: 270px; top: 180px; }
我尝试过在不同的requestAnimationFrame中分别设置top/left和transform属性,也试过使用translate3d,但都无法解决问题。请问有什么方法可以避免这种情况?
解决方案
方法1:用will-change提前告知浏览器优化
给目标元素添加will-change: transform,让浏览器提前为transform属性的变化分配资源,减少布局切换时的闪烁。修改.red类的CSS:
.red { background-color: red; transform-origin: 50% 50%; transition: transform 2s; position: absolute; will-change: transform; /* 新增 */ }
方法2:强制浏览器先完成布局计算
在设置top/left后,立即读取一个会触发重排的属性(比如offsetHeight),强制浏览器先完成top/left的布局更新,再设置transform。修改animate1函数:
function animate1() { let div = document.querySelector('div'); div.style.left = '0px'; div.style.top = '0px'; // 强制触发重排,让浏览器立即计算布局 void div.offsetHeight; div.style.transform = 'translate(120px, 100px) translate(150px, 80px)'; }
方法3:分渲染帧处理属性修改
将top/left和transform的修改放在两个连续的requestAnimationFrame中,给浏览器足够时间处理布局更新后,再应用transform变化:
function animate1() { let div = document.querySelector('div'); requestAnimationFrame(() => { div.style.left = '0px'; div.style.top = '0px'; requestAnimationFrame(() => { div.style.transform = 'translate(120px, 100px) translate(150px, 80px)'; }); }); }
内容的提问来源于stack exchange,提问作者Julijan Jug
相关产品推荐
相关产品推荐

