gpt4 book ai didi

javascript - 优化 DOM 元素的 native HitTest (Chrome)

转载 作者:IT王子 更新时间:2023-10-29 03:15:00 25 4
gpt4 key购买 nike

我有一个高度优化的 JavaScript 应用程序,一个高度交互的图形编辑器。我现在开始使用大量数据(图中有数千种形状)对其进行分析(使用 Chrome 开发工具),并且我遇到了以前不寻常的性能瓶颈, HitTest

| Self Time       | Total Time      | Activity            |
|-----------------|-----------------|---------------------|
| 3579 ms (67.5%) | 3579 ms (67.5%) | Rendering |
| 3455 ms (65.2%) | 3455 ms (65.2%) | Hit Test | <- this one
| 78 ms (1.5%) | 78 ms (1.5%) | Update Layer Tree |
| 40 ms (0.8%) | 40 ms (0.8%) | Recalculate Style |
| 1343 ms (25.3%) | 1343 ms (25.3%) | Scripting |
| 378 ms (7.1%) | 378 ms (7.1%) | Painting |

这占据了所有内容的 65% (!),在我的代码库中仍然是一个巨大的瓶颈。我知道这是跟踪指针下的对象的过程,我对如何优化它有一些无用的想法(使用更少的元素,使用更少的鼠标事件等)。

Context: The above performance profile shows a "screen panning" feature in my app, where the contents of the screen can be moved around by dragging the empty area. This results in lots of objects being moved around, optimized by moving their container instead of each object individually. I made a demo.


在开始这个之前,我想搜索优化 HitTest 的一般原则(那些不错的 ol'“No sh*t,Sherlock” 博客文章) ,以及是否存在任何技巧来提高这方面的性能(例如使用 translate3d 启用 GPU 处理)。

我试过类似 js optimize hit test 的查询,但搜索结果中充满了图形编程文章和手动实现示例——就好像 JS 社区以前听说过这件事一样!连 Chrome devtools guide缺少这个区域。

现在,我自豪地完成了我的研究,并提出疑问:我如何优化 JavaScript 中的原生 HitTest ?


I prepared a demo这证明了性能瓶颈,尽管它与我的实际应用程序完全不一样,而且数字显然也会因设备而异。查看瓶颈:

  1. 转到 Chrome(或您的浏览器的等效项)上的“时间轴”标签
  2. 开始录制,然后像疯子一样在演示中摇摄
  3. 停止记录并查看结果

回顾一下我在这方面已经完成的所有重要优化:

  • 在屏幕上移动单个容器,而不是单独移动数千个元素
  • 使用transform: translate3d 移动容器
  • 垂直同步鼠标移动到屏幕刷新率
  • 移除所有可能不必要的“wrapper”和“fixer”元素
  • 在形状上使用 pointer-events: none -- 无效

补充说明:

  • 瓶颈在没有GPU加速的情况下都存在
  • 测试仅在最新的 Chrome 中完成
  • DOM 是使用 ReactJS 呈现的,但没有它也可以观察到同样的问题,如链接的演示所示

最佳答案

有趣的是,pointer-events: none 没有效果。但是如果您考虑一下,这是有道理的,因为设置了该标志的元素仍然会掩盖其他元素的指针事件,因此无论如何都必须进行 hittest。

您可以做的是在关键内容上放置一个覆盖层并响应该覆盖层上的鼠标事件,让您的代码决定如何处理它。

这是可行的,因为一旦 hittest 算法找到了一个命中,并且我假设它会沿着 z-index 向下移动,它就会停止。


有叠加层

// ================================================
// Increase or decrease this value for testing:
var NUMBER_OF_OBJECTS = 40000;
// Wether to use the overlay or the container directly
var USE_OVERLAY = true;
// ================================================

var overlay = document.getElementById("overlay");
var container = document.getElementById("container");
var contents = document.getElementById("contents");

for (var i = 0; i < NUMBER_OF_OBJECTS; i++) {
var node = document.createElement("div");
node.innerHtml = i;
node.className = "node";
node.style.top = Math.abs(Math.random() * 2000) + "px";
node.style.left = Math.abs(Math.random() * 2000) + "px";
contents.appendChild(node);
}

var posX = 100;
var posY = 100;
var previousX = null;
var previousY = null;

var mousedownHandler = function (e) {
window.onmousemove = globalMousemoveHandler;
window.onmouseup = globalMouseupHandler;
previousX = e.clientX;
previousY = e.clientY;
}

var globalMousemoveHandler = function (e) {
posX += e.clientX - previousX;
posY += e.clientY - previousY;
previousX = e.clientX;
previousY = e.clientY;
contents.style.transform = "translate3d(" + posX + "px, " + posY + "px, 0)";
}

var globalMouseupHandler = function (e) {
window.onmousemove = null;
window.onmouseup = null;
previousX = null;
previousY = null;
}

if(USE_OVERLAY){
overlay.onmousedown = mousedownHandler;
}else{
overlay.style.display = 'none';
container.onmousedown = mousedownHandler;
}


contents.style.transform = "translate3d(" + posX + "px, " + posY + "px, 0)";
#overlay{
position: absolute;
top: 0;
left: 0;
height: 400px;
width: 800px;
opacity: 0;
z-index: 100;
cursor: -webkit-grab;
cursor: -moz-grab;
cursor: grab;
-moz-user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
user-select: none;
}

#container {
height: 400px;
width: 800px;
background-color: #ccc;
overflow: hidden;
}

#container:active {
cursor: move;
cursor: -webkit-grabbing;
cursor: -moz-grabbing;
cursor: grabbing;
}

.node {
position: absolute;
height: 20px;
width: 20px;
background-color: red;
border-radius: 10px;
pointer-events: none;
}
<div id="overlay"></div>
<div id="container">
<div id="contents"></div>
</div>

无覆盖

// ================================================
// Increase or decrease this value for testing:
var NUMBER_OF_OBJECTS = 40000;
// Wether to use the overlay or the container directly
var USE_OVERLAY = false;
// ================================================

var overlay = document.getElementById("overlay");
var container = document.getElementById("container");
var contents = document.getElementById("contents");

for (var i = 0; i < NUMBER_OF_OBJECTS; i++) {
var node = document.createElement("div");
node.innerHtml = i;
node.className = "node";
node.style.top = Math.abs(Math.random() * 2000) + "px";
node.style.left = Math.abs(Math.random() * 2000) + "px";
contents.appendChild(node);
}

var posX = 100;
var posY = 100;
var previousX = null;
var previousY = null;

var mousedownHandler = function (e) {
window.onmousemove = globalMousemoveHandler;
window.onmouseup = globalMouseupHandler;
previousX = e.clientX;
previousY = e.clientY;
}

var globalMousemoveHandler = function (e) {
posX += e.clientX - previousX;
posY += e.clientY - previousY;
previousX = e.clientX;
previousY = e.clientY;
contents.style.transform = "translate3d(" + posX + "px, " + posY + "px, 0)";
}

var globalMouseupHandler = function (e) {
window.onmousemove = null;
window.onmouseup = null;
previousX = null;
previousY = null;
}

if(USE_OVERLAY){
overlay.onmousedown = mousedownHandler;
}else{
overlay.style.display = 'none';
container.onmousedown = mousedownHandler;
}


contents.style.transform = "translate3d(" + posX + "px, " + posY + "px, 0)";
#overlay{
position: absolute;
top: 0;
left: 0;
height: 400px;
width: 800px;
opacity: 0;
z-index: 100;
cursor: -webkit-grab;
cursor: -moz-grab;
cursor: grab;
-moz-user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
user-select: none;
}

#container {
height: 400px;
width: 800px;
background-color: #ccc;
overflow: hidden;
}

#container:active {
cursor: move;
cursor: -webkit-grabbing;
cursor: -moz-grabbing;
cursor: grabbing;
}

.node {
position: absolute;
height: 20px;
width: 20px;
background-color: red;
border-radius: 10px;
pointer-events: none;
}
<div id="overlay"></div>
<div id="container">
<div id="contents"></div>
</div>

关于javascript - 优化 DOM 元素的 native HitTest (Chrome),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41830529/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com