javascript - 高频DOM突变
问题描述
我已经建立了一个简单的计时器,但我希望它尽可能快速和准确。换句话说,我想要最好的刷新率,同时保持它的精度,我的意思是秒的数字应该每 1000 毫秒改变一次,分钟应该每 60 秒改变一次,等等。
天文钟本身很容易制作,但当我试图让它快速制作时,我会出现奇怪的行为。
起初,计时器只是停留在 00:00,甚至不会改变。但我很快意识到这是因为我没有给浏览器任何时间来在每次执行更新和格式化时间的函数之间渲染元素。所以我试图做到这一点,以便每次下一次更新都在 0 毫秒超时后进行。这样,我认为它会将下一次更新的执行放在执行堆栈的末尾,这样浏览器就会在执行下一次更新函数之前渲染元素。
超时解决了我的问题,但只是部分解决了。计时器似乎工作正常,直到我意识到每隔一段时间就会跳过一秒。
我尝试在超时中添加一个 5 毫秒的小延迟,但仍然跳过了几秒钟,尽管每个跳过的秒之间的延迟更大。我尝试使用超时延迟,但即使延迟超过 50 毫秒,仍然会跳过几秒钟。我什至尝试使用该queueMicrotask
功能并将其与超时相结合,但它没有帮助。
在这一点上,我试图找到另一个解决方案,但我找不到任何东西。我不明白为什么会发生这种情况,所以如果有人能向我解释发生了什么以及如何解决这个问题,我将不胜感激。
注意:每个跳过的秒数之间的延迟不是恒定的,出于某种原因,1 分钟后会更频繁地跳过秒数。
计时器
const START_TIME = Date.now();
function formatTime(ms) {
ms = Math.round(ms * 1);
let secs = Math.trunc(ms / 1e3);
ms = (ms / 1e3 - secs) * 1e3;
let mins = Math.trunc(secs / 60);
secs = (secs / 60 - mins) * 60;
let hours = Math.trunc(mins / 60);
mins = (mins / 60 - hours) * 60;
hours = String(Math.trunc(hours));
mins = String(Math.trunc(mins));
secs = String(Math.trunc(secs));
ms = String(Math.trunc(ms));
const pad = (t, i = 2) => t.padStart(i, "0");
return `${pad(hours)}:${pad(mins)}:${pad(secs)},${pad(ms, 3)}`;
}
(() => {
const el = document.querySelector('.time');
const update = () => {
el.innerText = formatTime(Date.now() - START_TIME);
setTimeout(update);
}
update();
})();
@import url("https://fonts.googleapis.com/css2?family=Inconsolata:wght@300&display=swap");
body {
background-color: #222;
color: #35E;
margin: 0;
padding: 0;
}
.wrapper {
font-family: "Inconsolata", monospace;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
font-size: calc(4vw + 4vh + 10vmin);
height: 100vh;
width: 100vw;
display: flex;
justify-content: center;
align-items: center;
}
<div class="wrapper">
<div class="time"></div>
</div>
更新:
我尝试了`requestAnimationFrame`,但仍然得到相同的结果。
解决方案
performance.now()
您可以使用and获得更好的输出requestAnimationFrame
const START_TIME = performance.now();
function formatTime(ms) {
ms = Math.round(ms * 1);
let secs = Math.trunc(ms / 1e3);
ms = (ms / 1e3 - secs) * 1e3;
let mins = Math.trunc(secs / 60);
secs = (secs / 60 - mins) * 60;
let hours = Math.trunc(mins / 60);
mins = (mins / 60 - hours) * 60;
hours = String(Math.trunc(hours));
mins = String(Math.trunc(mins));
secs = String(Math.trunc(secs));
ms = String(Math.trunc(ms));
const pad = (t, i = 2) => t.padStart(i, "0");
return `${pad(hours)}:${pad(mins)}:${pad(secs)},${pad(ms, 3)}`;
}
(() => {
const el = document.querySelector('.time');
const update = () => {
el.innerText = formatTime(performance.now() - START_TIME);
requestAnimationFrame(update);
}
update();
})();
@import url("https://fonts.googleapis.com/css2?family=Inconsolata:wght@300&display=swap");
body {
background-color: #222;
color: #35E;
margin: 0;
padding: 0;
}
.wrapper {
font-family: "Inconsolata", monospace;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
font-size: calc(4vw + 4vh + 10vmin);
height: 100vh;
width: 100vw;
display: flex;
justify-content: center;
align-items: center;
}
<div class="wrapper">
<div class="time"></div>
</div>
我个人会像这样计算时间
const START_TIME = performance.now();
function formatTime(duration) {
let milliseconds = parseInt((duration % 1000));
let seconds = Math.floor((duration / 1000) % 60);
let minutes = Math.floor((duration / (1000 * 60)) % 60);
let hours = Math.floor((duration / (1000 * 60 * 60)) % 24);
hours = (hours < 10) ? "0" + hours : hours;
minutes = (minutes < 10) ? "0" + minutes : minutes;
seconds = (seconds < 10) ? "0" + seconds : seconds;
milliseconds = (milliseconds < 10) ? "00" + milliseconds : milliseconds < 100 ? "0" + milliseconds : milliseconds;
return hours + ":" + minutes + ":" + seconds + "," + milliseconds;
}
(() => {
const el = document.querySelector('.time');
const update = () => {
el.textContent = formatTime(performance.now() - START_TIME);
requestAnimationFrame(update);
}
update();
})();
@import url("https://fonts.googleapis.com/css2?family=Inconsolata:wght@300&display=swap");
body {
background-color: #222;
color: #35E;
margin: 0;
padding: 0;
}
.wrapper {
font-family: "Inconsolata", monospace;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
font-size: calc(4vw + 4vh + 10vmin);
height: 100vh;
width: 100vw;
display: flex;
justify-content: center;
align-items: center;
}
<div class="wrapper">
<div class="time"></div>
</div>
推荐阅读
- python-3.x - 无法使用 python 2,7 虚拟环境进行 pip 安装
- python - 为什么长文本文件在 Spyder 中不起作用?
- xml - 跳过选择标签中一个标签的 XML 验证
- python-3.x - 无法导入 tensorflow:Anaconda 下 Spyder 中没有名为“tensorflow”的模块
- c# - Microsoft.Azure.EventGrid.Models.EventGridEvent 不能通过控制器操作反序列化
- yocto - 如何使用 git info 动态创建文件并将其包含在图像中并将其保存在构建系统中
- django - 给 ForeignKey 表单字段一个查询集的值
- kubernetes-ingress - 如何在 Rancher 中为 Kubernetes 集群设置入口?
- java - 如何使用 JAVA 流从对象列表中查找平均值
- php - preg_replace() 推文 url 到 id 标签的正则表达式不起作用