javascript - 加速改变 div 元素颜色的 JavaScript 函数构造的“led 显示”
问题描述
背景资料
对于我正在从事的项目,我需要模拟一排可以以二进制显示不同数字的 LED。使用大量的 div 元素border-radius: 50%
,我可以制作成行的“LED”。然后,JavaScript 函数可以应用不同的 css 类来更改每个元素的颜色,以模拟它是打开还是关闭。我拥有的另一个功能是工具提示,当您将鼠标悬停在 LED 上时,它会显示 LED 显示的数值。
问题
我遇到的问题是这种方法的性能。这是因为我的网页将使用几十个这样的行,并且每行的值都会在每次屏幕刷新时通过requestAnimationFrame()
. 目前,在我的笔记本电脑上更新页面上 LED 行的每个实例大约需要 5 毫秒。这绝对低于我们在 60Hz 时每帧的 16.6ms - 但是对于我的项目,每帧还需要完成许多其他事情,这确实为这些事情留下了很少的时间。
一个例子
我当前的实现示例可在此处的 js fiddle 上找到:https ://jsfiddle.net/dL6jq7or/25/ 。单击“基准”文本并检查控制台以查看将 LED 行更新 50,000 次所需的时间。您可以使用它来快速评估您提出的任何想法的性能。
注意:我每次都将显示设置为不同的值,以避免浏览器在检测到没有发生实际更改时可能会采取的任何巧妙技巧。这些 LED 行的实际应用几乎总是每次都会给它们一个不同的值,因此这使得基准更加现实。
我已经尝试过加快速度
我在对象中缓存了对 LED 的引用,led_strips
这提供了小的性能提升,因为浏览器不再需要document.getElementById()
每次都这样做。
我听说在元素中添加/删除 css 类比直接使用 JavaScript 更改其 css 属性要快。这就是代码添加或删除on
类以更改 LED 状态的原因。
我发现on
在尝试删除它(反之亦然)之前首先检查 led div 是否有这个类可以提高性能。
笔记
LED 灯条没有固定的尺寸——其中的 LED 数量应仅由其中led
包含的类 div 的数量决定。
并非所有 LED 灯条都有工具提示 - 如果它们应该有一个,容器 div 将具有tooltip-enabled
该类并包含一个tooltip-content
div。
您将看到我注释掉了display_number_on_leds
仅在工具提示可见时才更新工具提示的功能部分。此检查提供了很大的性能提升,但意味着如果您在更改后将鼠标悬停在条上,工具提示可能会显示错误的值。这是不可接受的。
更改显示数字的浏览器花费的大部分时间似乎是重新计算页面的布局/样式。避免这种情况肯定会提高性能,但我不知道该怎么做。
概括
谁能看到一种方法来加速display_number_on_leds
保留我目前拥有的功能的功能?如果这种方法不可能更快地工作,是否有任何替代方法可以更快(可能是画布?)。
解决方案
由于您没有在工具提示中设置任何 HTML,只有文本,您可以使用其中之一nodeValue
,textContent
或innertext
代替。nodeValue似乎在这里给出了最好的结果,尽管我在 Chrome 66 上的速度提高了 50% 以上。
运行您的原始代码(使用innerHTML
)10 次给我:
50,000 iterations: 483.121826171875ms
50,000 iterations: 430.60400390625ms
50,000 iterations: 431.64599609375ms
50,000 iterations: 454.813232421875ms
50,000 iterations: 428.945068359375ms
50,000 iterations: 451.15673828125ms
50,000 iterations: 436.782958984375ms
50,000 iterations: 432.094970703125ms
50,000 iterations: 551.495849609375ms
50,000 iterations: 442.85400390625ms
使用tooltip.childNodes[0].nodeValue = text;
50,000 iterations: 212.26611328125ms
50,000 iterations: 198.85595703125ms
50,000 iterations: 206.324951171875ms
50,000 iterations: 201.528076171875ms
50,000 iterations: 202.0048828125ms
50,000 iterations: 195.52685546875ms
50,000 iterations: 207.598876953125ms
50,000 iterations: 202.48291015625ms
50,000 iterations: 207.677001953125ms
50,000 iterations: 197.98583984375ms
使用tooltip.textContent = text;
50,000 iterations: 259.39892578125ms
50,000 iterations: 221.156005859375ms
50,000 iterations: 238.042236328125ms
50,000 iterations: 212.44189453125ms
50,000 iterations: 221.1201171875ms
50,000 iterations: 225.68212890625ms
50,000 iterations: 226.552001953125ms
50,000 iterations: 209.56494140625ms
50,000 iterations: 215.00439453125ms
50,000 iterations: 222.412109375ms
使用tooltip.innerText = text;
50,000 iterations: 208.27099609375ms
50,000 iterations: 196.996826171875ms
50,000 iterations: 204.372802734375ms
50,000 iterations: 208.3291015625ms
50,000 iterations: 266.80810546875ms
50,000 iterations: 203.071044921875ms
50,000 iterations: 208.48876953125ms
50,000 iterations: 206.7939453125ms
50,000 iterations: 203.4111328125ms
50,000 iterations: 214.489013671875ms
编辑
除了上述之外,您还可以做一些其他事情来提高性能。
1.
num_dec_digits
和num_hex_digits
不改变迭代之间的值,因为它们仅取决于 LED 的数量,因此可以计算这些值get_led_references()
:
function get_led_references(id) {
var ref = document.getElementById(id);
var has_tooltip = ref.children[0].className == "tooltip_content";
var leds = Array.prototype.slice.call(ref.children);
var references = {
leds: leds,
tooltip: has_tooltip ? leds.shift() : null
};
var log10_pow2 = Math.log10(Math.pow(2, leds.length));
references.num_dec_digits = Math.ceil(log10_pow2);
references.num_hex_digits = Math.ceil(log10_pow2 / Math.log10(16));
return references;
}
当然,如果这样做,则display_number_on_leds()
必须相应地进行更改。
2.
在get_padded_num()
函数中,您当前正在分配比您需要的更长的新字符串。至少对于您提供的示例数据,在填充字符之前的 while 循环似乎执行速度提高了 10-20%:
function get_padded_num(number, length, base) {
var str = number.toString(base);
while (length > str.length) {
str = '0' + str;
}
return str;
}
因此,通过添加修改,执行时间现在是:
50,000 iterations: 165.459716796875ms
50,000 iterations: 169.538818359375ms
50,000 iterations: 176.2109375ms
50,000 iterations: 170.885986328125ms
50,000 iterations: 167.305908203125ms
50,000 iterations: 169.608154296875ms
50,000 iterations: 168.797119140625ms
50,000 iterations: 175.070068359375ms
50,000 iterations: 165.182861328125ms
50,000 iterations: 169.580810546875ms
推荐阅读
- java - 如何在单击重置按钮后取消选中所有选中的复选框、取消选中单选按钮、清除标记的文本字段?
- c++ - C++:使用 unique_ptr 作为地图中的键
- php - laravel中具有相同搜索字符串的多列
- php - 如何在不单击 PHP 中的注销按钮的情况下自动注销时跟踪注销时间(结束会话时间或强制关闭浏览器的选项卡/窗口)
- python - 使用 URLlib 解析 HTML - 如何打印超过 1 个标签?
- mysql - MYSQL FULLTEXT 搜索多个关键字
- spring - org.hibernate.LockMode 类型无法解析
- javafx - 为什么 ComboBox 响应延迟
- svg - 编辑 SVG 以更改字符
- python - 如何从新创建的 csv 文件列中添加?