requestFullscreen 后等待回流结束

Bes*_*rks 2 html javascript reflow window-resize html5-fullscreen

好吧,伙计们,姑娘们,对于你们所有的巫师来说,这是一个棘手的问题......

我正在开发一个沉浸式网络应用程序,该应用程序会覆盖移动设备上的默认触摸滚动行为。内容被划分为使用 100% 视口的页面,并且通过在页面之间上下滑动来处理导航。

第一次滑动时,我调用requestFullscreen()body元素,这当然会在视口调整大小时导致回流。问题是我也希望第一次滑动来触发自定义滚动行为,但我正在使用Element.nextElementSibling.scrollIntoView({ block : start, behavior : 'smooth' }),直到回流完成,下一页的顶部边缘( )HTMLSectionElement已经可见,因此滚动不会发生。

如果我习惯setTimeout等待大约 600 毫秒直到回流完成,滚动效果会按预期工作,但我对这种 hacky 解决方法不满意,我更喜欢使用更优雅的异步解决方案。

我首先尝试从 requestFullscreen 返回的 Promise 执行器内部触发滚动效果,resolve但这没有帮助。这个承诺在执行流程的早期就得到了解决。

然后我从事件处理程序内部尝试fullscreenchange。这里也不走运,因为该事件在全屏更改发生之前立即触发。

最后,我尝试从窗口resize事件处理程序内部进行尝试,但这会在回流发生之前触发。我requestIdleCallback也在这里添加了一个,但没有任何区别。

所以我的问题是......有没有可靠的方法来检测回流操作的结束?或者...是否有人有更好的 B 计划,而不是放弃使用scrollIntoView我自己的滚动效果并将其编码到窗口调整大小处理程序中。

Bes*_*rks 6

好吧,未来的谷歌用户,几年后我回来了,对这个问题有一个真正的非黑客的答案。

诀窍是requestAnimationFrameResizeObserver. 第一个回调在回流发生之前触发。您可以使用此回调 this 对 DOM 进行最后一秒的更改。然后,在这个回调中,我们requestAnimationFrame再次使用来指定另一个回调,该回调将在前一帧的绘制之后发生。

debugger;您可以通过取消注释下面示例代码中的行来验证此方法的计时。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="description" content="React to element size changes after layout reflow has occured.">
    <title> Reflow Observer Test </title>
    <style>
      * { box-sizing: border-box; }
      html, body { height: 100vh; margin: 0; padding: 0; }
      body { background: grey; }
      body:fullscreen { background: orange;}
      body:fullscreen::backdrop { background: transparent; }
      #toggle { margin: 1rem; }
    </style>
  </head>
  <body>
    <button id="toggle"> Toggle Fullscreen </button>
    <script>
      let resizableNode = document.body;
      
      resizableNode.addEventListener('reflow', event => {
        console.log(event.timeStamp, 'do stuff after reflow here');
      });
      
      let observer = new ResizeObserver(entries => {
        for (let entry of entries) {
          requestAnimationFrame(timestamp => {
            // console.log(timestamp, 'about to reflow'); debugger;
            requestAnimationFrame(timestamp => {
              // console.log(timestamp, 'reflow complete'); debugger;
              entry.target?.dispatchEvent(new CustomEvent('reflow'));
            });
          });
        }
      });
      
      observer.observe(resizableNode);
      
      function toggleFullscreen(element) {
        if (document.fullscreenElement) {
          document.exitFullscreen();
        } else {
          element.requestFullscreen();
        }
      }
      
      document.getElementById('toggle').addEventListener('click', event => {
        toggleFullscreen(resizableNode);
      });
    </script>
  </body>
</html>
Run Code Online (Sandbox Code Playgroud)

在此示例中,我使用 body 元素上的全屏作为触发器,但这可以轻松应用于任何 DOM 节点上的任何调整大小操作。

  • 我已将其包装成一个漂亮整洁的 ES6 类,可在此处获取 https://github.com/besworks/ReflowObserver (2认同)