网站首页 > 技术文章 正文
有一天,我需要一个数字时钟组件,所以我很快编写了一个简单的 JavaScript 方法:
function uiDigitalClock(node) {
const now = () => {
node.textContent = new Date().toLocaleTimeString()
requestAnimationFrame(now)
}
now()
}
我在某处读到,这requestAnimationFrame应该比 更好的性能setInterval,但该方法一直困扰着我。
在最佳条件下,每秒requestAnimationFrame触发60 次——这比数字时钟需要的多 60 倍!
Date()每秒创建 60 次对象对性能来说并不好!
所以我尝试了setInterval:
function interval(node){
return setInterval(() => node.textContent = new Date().toLocaleTimeString(), 1000)
}
同样,代码不多,视觉结果是相同的 - 并且每秒只有一个对象。 Date()
又怎样呢setTimeout()?
function timeout(node) {
const now = () => {
node.textContent = new Date().toLocaleTimeString()
setTimeout(now, 1000)
}
now()
}
它也能得到类似的结果,但有令人讨厌的延迟(setImmediate()仅在 Node 中)
然后我决定修改初始版本,只每秒触发一次,并requestAnimationFrame()返回时间戳:
function frame(node) {
let last = 0;
const render = now => {
if (!last || now - last >= 1000) {
last = now;
node.textContent = new Date().toLocaleTimeString();
}
requestAnimationFrame(render);
}
window.requestAnimationFrame(render);
}
当所有 3 个方法都运行时,我碰巧检查了开发工具中的标记,并注意到我什至无法扩展节点,因为它们不断刷新,并且看起来像旧的<blink>-tag!
向巴德寻求帮助
仍然不知道该选择哪种方法,我决定询问 Google Bard,它回答说:
- Clock1使用该setInterval()方法创建一个每 1000 毫秒(1 秒)触发一次的循环计时器。这是性能最低的方法,因为它为每次迭代创建一个新的计时器,这可能会增加大量的开销。
- Clock2使用该setTimeout()方法,创建一个在 1000 毫秒后触发的计时器。这比clock1 性能更高,因为它不会为每次迭代创建新的计时器。然而,它的性能仍然不如clock3,因为浏览器必须等待计时器触发才能更新文本内容。
- Clock3使用该requestAnimationFrame()方法,这是请求浏览器重绘的更有效方法。浏览器只会在必要时重新绘制元素,这可以节省大量的 CPU 时间。
好吧,我决定使用clock3 ...但后来我突然想到:“ CSS 中的数字动画怎么样?使用@property?
Jhey不久前制作了一个纯 CSS 秒表,所以我决定尝试类似的东西。
@property --seconds {
syntax: "<integer>";
initial-value: 0;
inherits: false;
}
@property --minutes {
syntax: "<integer>";
initial-value: 0;
inherits: false;
}
@property --hours {
syntax: "<integer>";
initial-value: 0;
inherits: false;
}
然后,在<ol>-tag 中,我<li>为每个时间单位添加了一个 -tag。
要使用 - 声明的值@property,您需要使用 CSS 计数器,因此几秒钟内它是:
.seconds {
animation: seconds 60s steps(60, end) infinite;
animation-delay: var(--delay-seconds, 0s);
counter-reset: seconds var(--seconds);
&::after { content: counter(seconds, decimal-leading-zero) ' '; }
}
要为秒设置动画,需要一个关键帧:
@keyframes seconds {
from { --seconds: 0;}
to { --seconds: 60; }
}
对于几分钟来说,几乎是一样的,但是动画花费了 60 倍的时间 (60 x 60 = 3600):
animation: minutes 3600s steps(60, end) infinite;
对于几个小时,我们需要将该数字乘以 24:
animation: hours 86400s steps(24, end) infinite;
耶!我们有一个可以工作的 CSS 时钟……但它只在午夜工作,因为小时、分钟和秒都从0(零)开始。
那么该怎么办?创建初始对象后,我可以轻松地从 JavaScript 更新属性Date()。
但这样动画就会出错,因为它们会运行相同的时间(每秒钟 60 秒),即使实际的秒数小于该值。
我在 Twitter 上寻求帮助——幸运的是,Temani Afif 和 álvaro Montoro 回复了!解决方案是使用负数 animation-delay。
因此,使用一些 JavaScript 来设置当前时间并计算延迟:
const time = new Date();
const hours = time.getHours();
const minutes = time.getMinutes();
const seconds = time.getSeconds();
// Delays
const HOURS = -Math.abs((hours * 3600) + (minutes * 60) + seconds);
const MINS = -Math.abs((minutes * 60) + seconds);
const SECS = -Math.abs(seconds);
...我们可以更新之前指定的 CSS 属性,例如:
node.style.setProperty(`--delay-seconds`, `${seconds}s`);
现在,我们有了一个可以工作的数字 CSS 时钟——将其与此处的其他方法进行比较:
如果您在开发工具中检查标记,您会发现 CSS 版本并未重写 DOM 内容。
倒数
之后,我决定重新审视我的旧 Codepen,多语言倒计时,并制作一个纯 CSS 版本:
locale如果您想要其他语言,您可以在 JS 代码中使用:
但性能呢?CSS 可能不会像 JavaScript 那样阻塞主线程,但我们能确定它使用 GPU 而不是 CPU 吗?
有一个老技巧:
.useGpu {
transform: translateZ(0);
will-change: transform;
}
然后,在开发工具中,转到“图层”:
看到“倒计时”现在如何拥有自己的渲染层了吗?不确定这是否仍然适用,但我猜添加也没什么坏处。
离开浏览器选项卡
当我离开浏览器选项卡并返回时,纯 CSS 时钟没有出现任何问题。也许是我等的时间还不够长吧!但如果您遇到任何问题,请使用此事件重新计算时钟的延迟:
document.addEventListener('visibilitychange', () => {
if (!document.hidden) { ... }
})
模拟时钟
作为奖励 - 这是一个模拟时钟,我不久前做了:
猜你喜欢
- 2024-11-14 Python Web全栈之旅09--Web前端●CSS浮动
- 2024-11-14 CSS 面试题:CSS的权重与优先级 css权重和优先级
- 2024-11-14 HTML/CSS 备忘录 - 12. CSS 浮动与定位
- 2024-11-14 Web前端开发-CSS布局-浮动和定位-入门干货
- 2024-11-14 前端初学者必看,这10 个CSS3 属性,你需要熟悉
- 2024-11-14 CSS学习之权重 css权重的计算方式
- 2024-11-14 css 绘制心形 css版心
- 2024-11-14 CSS 函数那些事(二)你不知道的 attr()
- 2024-11-14 如何解决after和before的兼容性 before和after用法
- 2024-11-14 CSS-西安钟楼 西安钟楼视频讲解
- 标签列表
-
- content-disposition (47)
- nth-child (56)
- math.pow (44)
- 原型和原型链 (63)
- canvas mdn (36)
- css @media (49)
- promise mdn (39)
- readasdataurl (52)
- if-modified-since (49)
- css ::after (50)
- border-image-slice (40)
- flex mdn (37)
- .join (41)
- function.apply (60)
- input type number (64)
- weakmap (62)
- js arguments (45)
- js delete方法 (61)
- blob type (44)
- math.max.apply (51)
- js (44)
- firefox 3 (47)
- cssbox-sizing (52)
- js删除 (49)
- js for continue (56)
- 最新留言
-