当前位置:  开发笔记 > 编程语言 > 正文

优化DOM元素的本机命中测试(Chrome)

如何解决《优化DOM元素的本机命中测试(Chrome)》经验,为你挑选了2个好方法。

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

| 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%(!),仍然是我的代码库中的一个怪物瓶颈.我知道这是在指针下跟踪对象的过程,我对如何优化它有无用的想法(使用更少的元素,使用更少的鼠标事件等).

上下文:上面的性能配置文件在我的应用程序中显示"屏幕平移"功能,其中可以通过拖动空白区域来移动屏幕内容.这导致大量对象被移动,通过移动其容器而不是单独移动每个对象进行优化.我做了一个演示.


在进入这个之前,我想寻找优化命中测试的一般原则(那些好的'' 没有sh*t,Sherlock'博客文章),以及是否存在任何提高性能的技巧(例如使用translate3d,以使GPU处理).

我尝试过像js optimize hit test这样的查询,但结果充满了图形编程文章和手动实现示例 - 就好像JS社区之前甚至没有听说过这个东西!即使是chrome devtools指南也缺乏此区域.

编辑:有这个问题,但它没有多大帮助:什么是Chrome开发工具"命中测试"时间表条目?

所以在这里,我自豪地完成了我的研究,询问:如何在JavaScript中优化本机命中测试?


我准备了一个演示性能瓶颈的演示,虽然它与我的实际应用程序并不完全相同,但数字显然会因设备而异.要看到瓶颈:

    转到Chrome上的时间轴标签(或相当于您的浏览器)

    开始录制,然后像疯子一样在演示中平移

    停止录制并检查结果


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

在屏幕上移动单个容器而不是单独移动数千个元素

使用transform: translate3d移动容器

v-同步鼠标移动到屏幕刷新率

删除所有可能不必要的"包装器"和"修复器"元素

pointer-events: none在形状上使用- 没有效果

补充说明:

瓶颈都存在没有 GPU加速

测试只在最新的Chrome中完成

DOM是使用ReactJS呈现的,但是没有它可以观察到相同的问题,如链接演示中所示

Manuel Otto.. 10

有趣,pointer-events: none没有效果.但是如果你考虑它,它是有道理的,因为具有该标志集的元素仍然模糊了其他元素的指针事件,所以无论如何必须发生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;
}

没有叠加

// ================================================
// 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;
}



1> Manuel Otto..:

有趣,pointer-events: none没有效果.但是如果你考虑它,它是有道理的,因为具有该标志集的元素仍然模糊了其他元素的指针事件,所以无论如何必须发生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;
}


2> Vinícius Neg..:

其中一个问题是你要移动容器内的每一个元素,如果你有GPU加速没关系,瓶颈就是重新计算它们的新位置,即处理器字段.

我的建议是分割容器,因此你可以单独移动各种窗格,减少负荷,这称为宽相计算,即只移动需要移动的东西.如果屏幕上有东西,你为什么要移动它?

从制作而不是一个16个容器开始,你将不得不在这里做一些数学运算来找出哪些窗格正在显示.然后,当鼠标事件发生时,仅移动那些窗格并将未显示的窗格保留在原来的位置.这应该大大减少用于移动它们的时间.

+------+------+------+------+
|    SS|SS    |      |      |
|    SS|SS    |      |      |
+------+------+------+------+
|      |      |      |      |
|      |      |      |      |
+------+------+------+------+
|      |      |      |      |
|      |      |      |      |
+------+------+------+------+
|      |      |      |      |
|      |      |      |      |
+------+------+------+------+

在这个例子中,我们有16个窗格,其中2个正在显示(由S标记为屏幕).当用户平移时,检查"屏幕"的边界框,找出哪些窗格属于"屏幕",只移动那些窗格.这在理论上是可无限扩展的.

不幸的是,我没有时间编写显示思想的代码,但我希望这会对你有所帮助.

干杯!

推荐阅读
有风吹过best
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有