在浏览器中检测重绘/回流是否可能?

3

不确定是否有解决此问题的方法。我想知道是否有一种方法可以检测浏览器实际更新UI的时间。

假设我想在运行长时间同步代码块之前向元素添加一个类,在同步函数运行后,该类将被删除。

el.classList.add('waiting');
longRunningSynchronousTask();
el.classList.remove('waiting');

如果我运行这段代码,长时间的任务将会启动,并在添加类之前阻止UI。我尝试使用MutationObservergetComputedStyle检查元素,但这并不起作用,因为它们不能反映出实时更新UI的情况。
使用worker可能是最好的解决方案(将长时间运行的代码放入worker中,并在其完成后发布消息以删除类)。对于我来说,这不是一个真正的选项,因为需要支持旧版浏览器。
除了实现worker外,唯一可行的解决方案是使用setTimeout。我想知道是否有人遇到过这个问题,是否有任何方法可以检测浏览器中UI的重绘/回流。
请查看我的示例,以更好地说明问题。

const el = document.querySelector('.target');
const isRed = c => c === 'red' || 'rgb(255, 0, 0)';

let classActuallyApplied = false;

let requestId;

const longRunningTask = (mil, onComplete) => {
  if (classActuallyApplied) {
    cancelAnimationFrame(requestId);
    var start = new Date().getTime();
    while (new Date().getTime() < start + mil);
    onComplete();
  } else {
     requestId = requestAnimationFrame(longRunningTask.bind(null, mil, onComplete));
  }
}

const observer = new MutationObserver(function(mutations) {
 classActuallyApplied = mutations
   .filter(mutation => {
   return mutation.type === 'attributes' &&
       mutation.attributeName === 'class' &&
        mutation.target.classList.contains('waiting');
  }).length > 0;
});

const observerConfig = {
 attributes: true, 
 childList: false, 
 characterData: false 
};

observer.observe(el, observerConfig);

el.addEventListener('click', e => {
 el.classList.add('waiting');
  longRunningTask(1000 * 5, () => {
   // el.classList.remove('waiting');
  });
});
.target {
  height: 100px;
  width: 100px;
  background-color: gray;
}

.waiting {
  background-color: red;
}
<div class="target">
  target
</div>


你想检测它还是触发它?我不明白在你的情况下检测的意义是什么。 - Denys Séguret
如果您知道在代码中导致UI更新的条件,只需在那里添加适当的调用。如果是基于窗口大小调整,则只需添加相应的监听器。 - Andre M
@DenysSéguret 我想要检测界面何时被更新。如果我添加了 waiting 类,例如,将会把文字颜色改成红色。我想知道用户界面何时已经被更新。一旦它被更新,我就可以开始我的长时间运行任务。 - Nicholas Mitchell
使用0作为setTimeout的毫秒参数不够好吗?将您的长时间同步任务放在一个以0毫秒为时间参数的setTimeout中,并将您的函数作为第一个参数。 - elad.chen
@elad.chen 是的。那可能已经足够了。那是我的最初解决方案。但我很好奇,为什么我发现我无法检测到UI何时已更新。 - Nicholas Mitchell
稍微退后一步,从不同的角度看待问题:是什么导致了UI的更新?例如你的代码、用户交互或其他原因。你关心哪些更新? - Andre M
1个回答

4

我相信requestIdleCallback就是你要找的。

来自developers.google.com 2016

与采用requestAnimationFrame允许我们正确安排动画并最大化命中60fps的机会一样,当帧末尾有空闲时间或用户处于非活动状态时,requestIdleCallback将安排工作。

这是一个实验性功能,但(截至2021年3月)在大多数现代浏览器中都得到了支持。请参见MDN Compatibility Tablecaniuse.com


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接