更改 getBoundingClientRect() 的事件或观察者
Posted
技术标签:
【中文标题】更改 getBoundingClientRect() 的事件或观察者【英文标题】:An event or observer for changes to getBoundingClientRect() 【发布时间】:2017-03-08 03:48:46 【问题描述】:有没有一种方法可以检测元素的getBoundingClientRect()
矩形何时发生变化而无需实际计算getBoundingClientRect()
?像“脏旗”之类的东西?天真地,我假设在浏览器的内部工作中一定有这样的机制,但我一直无法找到暴露在 DOM API 中的这个东西。也许有办法用 MutationObservers 做到这一点?
我的应用程序是一个 Web 组件,它将 DOM 元素转换为图形的节点,并将边缘绘制到全屏画布上。见here。
现在,我为每个元素调用getBoundingClientRect()
,每个动画帧帧一次,即使没有任何变化。感觉很贵我通常在一台功能强大的计算机上以 60 fps 的速度获得 %15-%50 的 CPU 使用率。
有人知道这样的事情吗?你认为期待这样的事情合理吗?这种事情可行吗?以前有人提出过吗?
【问题讨论】:
您正在寻找ResizeObserver。另见IntersectionObserver。 @wOxxOm 哇...显然这是最前沿的东西! 它不检测重新定位。在拖动元素的情况下,如果您可以确保父元素不会移动/调整大小,则只需使用 offsetLeft 和 offsetTop。 我的意思是你可以只计算一次父母的位置,然后使用孩子的offsetLeft和offsetTop。并考虑 window.scrollX & Y。 @wOxxOm,您是否建议在父级上执行 getBoundingClientRect(),然后在父级的子级上使用 offsetLeft 和 offsetTop?我认为这对于相对定位的孩子来说是个好主意,但在这种情况下,我已经绝对定位了孩子。你的方法听起来确实更有效率,但它太复杂了,不能作为通用解决方案。 【参考方案1】:如上面的 cmets 中所述。您正在寻找的 API 是:ResizeObserver
和 IntersectionObserver
。但是,有几点需要注意:
ResizeObserver
只会在观察到的元素改变大小时触发。而且它基本上只会为您提供正确的宽度和高度值。
ResizeObserver
和 IntersectionObserver
都应该不会阻挡油漆
ResizeObserver
将在布局之后但在绘制之前触发,这基本上使它感觉同步。
IntersectionObserver
异步触发。
如果您需要位置变化跟踪怎么办
这就是 IntersectionObserver
的用途。它通常可以用于可见性检测。这里的问题是IntersectionObserver
仅在交集比率发生变化时触发。这意味着如果一个小的孩子在一个较大的容器 div 中四处移动,并且您跟踪父级和子级之间的交集,则除了孩子进入或退出父级时,您不会收到任何事件。
您仍然可以跟踪元素何时移动。方法如下:
首先使用getBoundingClientRect
测量您要跟踪的元素的位置。
插入一个 div 作为 body 的绝对定位的直接子代,该子代正好位于被跟踪元素所在的位置。
开始跟踪这个 div 和原始元素的交集。
交叉点应该从 1 开始。每当它变成其他东西时:
使用getBoundingClientRect
重新测量元素。
触发位置/大小更改事件
将自定义 div 的样式更新到元素的新位置。
观察者应该再次触发,交叉比再次为 1,这个值可以忽略。
注意:此技术也可用于为ResizeObserver
提供更有效的 polypill,这是一个比IntersectionObserver
更新的功能。常用的 polyfill 依赖于 MutationObserver
,但效率要低得多。
【讨论】:
你有你方法的例子吗?鉴于绝对定位的元素是 body 的直接子元素,我看不出这将如何工作,但 IntersectionObserver 会要求它是我们要跟踪的元素的后代 @MarcS 如果被跟踪元素没有position: relative
,那么绝对定位的子元素将相对于确实拥有position: relative;
的某个祖先进行定位。我认为被跟踪的元素需要两个绝对定位的子元素(一个在左上角,另一个在右下角)才能工作(如果它只在左上角,那么如果被跟踪元素向左移动 1px 则交叉点观察器不会触发。
你会如何做“开始跟踪这个 div 和原始元素之间的交集”。步?这对于传统的 IntersectionObserver
来说似乎是不可能的,因为这两个元素都不是另一个元素的祖先。以上是关于更改 getBoundingClientRect() 的事件或观察者的主要内容,如果未能解决你的问题,请参考以下文章
Element.getBoundingClientRect()
getBoundingClientRect();不工作..?