Zol*_*tók 3 html css browser resize
我为一个小型益智游戏显示了一个点网格。该脚本还不太正确,但这不是问题所在。
问题是,对于所有这些 div,调整浏览器窗口的大小非常缓慢。尽管没有在调整大小时重绘/重新定位元素之类的事情。
在 Safari 和 Chrome 中都尝试过。也尝试了一下grid,结果与绝对定位相同。
需要一个大窗口来生成足够的 div(~5000)才能显示问题。
我们不应该有这么多 div 吗?有没有办法优化这个?也许有一个大型元素网格的替代解决方案?
对于我想要完成的任务来说,使用 Three.js 感觉有点矫枉过正。
谢谢。
编辑:这是一个 JSBin 示例,您可以尝试最大化窗口(然后重新运行)以使问题显现出来。显示alert生成的 div 数量。https://jsbin.com/zurojakese/edit?output。
let size = 10
let gap = 10
let cols = (window.innerWidth - gap) / (size + gap)
let rows = (window.innerHeight - gap) / (size + gap)
let col = 0
let row = 0
for (let i = 0; i < cols * rows; i++) {
let div = document.createElement('div')
document.body.appendChild(div)
div.style.top = gap + row * (gap + size) + 'px'
div.style.left = (row % 2 == 0 ? gap : 0) + gap + col * (gap + size) + 'px'
div.style.animationDelay = (0.25 * i) + 's'
col += 1
if (col > cols) {
col = 0;
row += 1
}
}Run Code Online (Sandbox Code Playgroud)
div {
position: absolute;
background: #adadad;
width: 10px;
height: 10px;
border-radius: 5px;
}Run Code Online (Sandbox Code Playgroud)
出现此问题的原因是浏览器在调整大小操作期间正在页面上进行多次布局计算和重新绘制。
在调整窗口大小时,浏览器通常会对DOM进行完全回流和重绘。回流焊是一个非常昂贵的过程,特别是对于大量节点而言。(在您的情况下,节点已绝对定位,因此它们不会执行完整的回流操作,但仍会对每个节点进行布局计算。)重新绘制是也会发生的后续操作,并且比回流或布局快得多计算,仍然会增加窗口调整大小所需的计算时间。回流、布局计算和重绘是用户阻塞过程:即用户交互发生时会被中断,并且会同步进行。
虽然某些回流(例如由特定元素上的 JavaScript 触发的回流)可以在节点子集上进行,但窗口大小调整预计会触发 DOM 中所有显示节点的回流(或固定和绝对元素的布局计算) 。在某些硬件上,> 5000 个节点的回流花费的时间比一帧(16 毫秒)更长,这并非不合理。拖动窗口边框来调整大小将触发一系列非常快速的回流,这些回流全部被阻塞,因此您会看到性能问题。
浏览器确实有一些优化来最大限度地减少不必要的回流,但不幸的是,您可能不走运。但是,有两种可能的解决方法可以解决您的问题。
设置display: none将从回流计算中删除元素及其子元素。第一次发生事件时resize,将最外面的元素设置为display: none隐藏游戏。回流计算应该大大加快。调整大小完成后,设置display: block(或任何原始display值)。您可以调整去抖函数来确定调整大小何时完成 - 我发现 50 毫秒没有新resize事件是用户已停止调整大小的良好指标。
这显然意味着在调整大小时会出现空白屏幕。为了获得更无缝的外观,您可能需要在调整大小过程中显示通知。或者,您可以使用html2canvas等库在删除游戏元素之前立即截取页面的屏幕截图,并在调整大小期间显示该屏幕截图。请注意,由于节点数量如此之多,使用 html2canvas 可能会在捕获图像时导致初始滞后。
运行具有数千个 HTML 节点的游戏无疑突破了 HTML 的用途极限。使用 HTML Canvas 可能是一种更合适的方法,尽管听起来它需要对代码进行大量修改。您可以控制画布重绘完成的每次情况,因此调整窗口大小不需要任何重绘。在游戏中使用 Canvas 还有许多其他好处,例如它使用 GPU 而不是 CPU 进行绘图,但这超出了本问题的范围。