javascript - requestFullscreen 后等待回流结束
问题描述
好吧,伙计们,这对你们所有的巫师来说都是一个棘手的问题......
我正在开发一个沉浸式网络应用程序,它会覆盖移动设备上的默认触摸滚动行为。内容被划分为使用 100% 视口的页面,并且通过在页面之间上下滑动来处理导航。
在第一次滑动时,我调用requestFullscreen()
了body
元素,当视口调整大小时,它当然会导致回流。问题是我还希望第一次滑动来触发自定义滚动行为,但我正在使用Element.nextElementSibling.scrollIntoView({ block : start, behavior : 'smooth' })
,直到回流完成,下一页的顶部边缘(an HTMLSectionElement
)已经可见,因此不会发生滚动。
如果我setTimeout
习惯等待大约 600 毫秒直到回流完成,滚动效果会按预期工作,但我对这种 hacky 解决方法不满意,我更喜欢使用更优雅的异步解决方案。
我首先尝试从 requestFullscreen 返回的 Promise 的执行程序内部触发滚动效果,resolve
但这没有帮助。这个承诺在执行流程的早期就解决了。
fullscreenchange
然后我从事件处理程序内部尝试。这里也没有运气,因为此事件在全屏更改发生之前立即触发。
最后,我从窗口事件处理程序内部进行了尝试,resize
但这会在回流发生之前触发。我requestIdleCallback
也在这里添加了一个,但没有任何区别。
所以我的问题是......有没有可靠的方法来检测回流操作的结束?或者...有没有人有比放弃使用scrollIntoView
我自己的滚动效果并将我自己的滚动效果编码到窗口调整大小处理程序中更好的 B 计划。
解决方案
好吧,未来的谷歌人,几年后我回来了,对这个问题给出了一个真正的、非骇人听闻的答案。
诀窍是requestAnimationFrame
在ResizeObserver
. 第一个回调在回流发生之前触发。您可以使用此回调 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>
在此示例中,我使用 body 元素上的全屏作为触发器,但这可以轻松应用于任何 DOM 节点上的任何调整大小操作。
推荐阅读
- javascript - 如何在 Firebase Firestore 中将 Web 表单(Javascript)条目添加或保存为 GeoPoint 类型
- html - 自定义滑块定位元素。(HTML + CSS)
- node.js - Heroku 应用程序抛出错误:自签名证书
- docker - 我可以在单机上安装 Kubernetes 集群吗?
- xcode - Flutter:无法构建 iOS 项目
- javascript - forEach 无需传递参数即可工作?
- python - 1.0 的准确度,而训练损失和验证损失仍在下降
- java - 有人可以解释什么是 SVN 平分算法吗?理论上和通过代码片段
- javascript - 从firebase数据库读取并存储数据,返回值用于其他页面
- curl - 从 \"\" 解析正文失败,因为无效字符 'G' 正在寻找对象键字符串的开头