更改 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 是:ResizeObserverIntersectionObserver。但是,有几点需要注意:

ResizeObserver 只会在观察到的元素改变大小时触发。而且它基本上只会为您提供正确的宽度和高度值。 ResizeObserverIntersectionObserver 都应该不会阻挡油漆 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() 的事件或观察者的主要内容,如果未能解决你的问题,请参考以下文章

getBoundingClientRect()方法

Element.getBoundingClientRect()

getBoundingClientRect();不工作..?

js中getBoundingClientRect()方法详解

js中getBoundingClientRect( )方法

JS获得元素相对位置坐标getBoundingClientRect()