如何在CSS动画暂停时立即获取元素的getBoundingClientRect坐标?
如何在CSS动画暂停瞬间获取元素精确坐标
问题描述
我需要在通过CSS Animation移动的元素暂停时获取其正确坐标。动画运行期间,getBoundingClientRect()的返回值持续变化,目标是在动画暂停瞬间拿到元素的精确坐标值。
尝试暂停动画后立即获取坐标,但结果不准确。目前用setTimeout延迟1秒能拿到正确值,但希望无需延迟,在暂停的同时就获取准确坐标。
代码示例
JavaScript
let el = document.getElementById('el'); let stopCoords = document.getElementById('stopCoords'); let currentCoords = document.getElementById('currentCoords'); let thinkCoords = document.getElementById('thinkCoords'); let stopCoordsText = document.getElementById('stopCoordsText'); let currentCoordsText = document.getElementById('currentCoordsText'); let thinkCoordsText = document.getElementById('thinkCoordsText'); stopCoords.addEventListener('click',()=>{ el.classList.toggle('stop') let coordsX = el.getBoundingClientRect().x; stopCoordsText.innerText = `${coordsX}`; }) currentCoords.addEventListener('click',()=>{ let coordsX = el.getBoundingClientRect().x; currentCoordsText.innerText = `${coordsX}`; }) thinkCoords.addEventListener('click',()=>{ el.classList.toggle('stop'); setTimeout(()=>{ let coordsX = el.getBoundingClientRect().x; thinkCoordsText.innerText = `${coordsX}`; },1000) })
CSS
#el { width: 2px; height: 20px; background: #000; animation-name: moving; animation-duration: 0.9s; animation-delay: none; animation-iteration-count: infinite; animation-play-state: running; animation-timing-function: ease; animation-fill-mode: forwards; } #el.stop{ animation-play-state: paused; } @keyframes moving{ 0% { transform:translate(0em); } 50%{ transform:translate(10em); } 100%{ transform:translate(0em); } } button { margin-top:100px; }
HTML
<div id="el"></div> <button id='stopCoords'>暂停动画并获取坐标</button> <button id='currentCoords'>获取当前坐标</button> <button id='thinkCoords'>尝试延迟获取坐标</button> <p>暂停时获取的坐标 : <span id='stopCoordsText'></span> </p> <p>当前坐标 : <span id='currentCoordsText'></span> </p> <p>延迟获取的坐标 : <span id='thinkCoordsText'></span> </p>
解决方案
问题根源是浏览器的渲染队列机制:修改animation-play-state后,浏览器不会立即更新元素布局,此时调用getBoundingClientRect()拿到的仍是旧值。以下两种方法可以解决:
方法1:使用requestAnimationFrame
将获取坐标的逻辑放到下一帧回调中,确保浏览器完成动画状态更新和布局计算后再执行:
stopCoords.addEventListener('click',()=>{ el.classList.toggle('stop'); requestAnimationFrame(() => { let coordsX = el.getBoundingClientRect().x; stopCoordsText.innerText = `${coordsX}`; }); })
方法2:强制触发浏览器重排
在获取坐标前,读取元素的offsetHeight或offsetWidth等会触发重排的属性,强制浏览器立即更新布局:
stopCoords.addEventListener('click',()=>{ el.classList.toggle('stop'); // 强制触发重排,更新布局 el.offsetHeight; let coordsX = el.getBoundingClientRect().x; stopCoordsText.innerText = `${coordsX}`; })
原理说明
浏览器会把DOM修改、样式变更等操作放入渲染队列批量执行,以提升性能。requestAnimationFrame的回调会在浏览器下一次重绘前执行,此时动画状态的变更已生效;而读取offsetHeight这类属性会强制浏览器立即执行队列中的布局计算,确保拿到最新的元素坐标。
内容的提问来源于stack exchange,提问作者sungtt
相关产品推荐
相关产品推荐

