首页 > 其他分享 >写个时钟(进阶篇)

写个时钟(进阶篇)

时间:2024-07-01 09:58:51浏览次数:3  
标签:写个 const -- transform 进阶篇 deg 设置 时钟 指针

在“写个时钟(行为篇)”中,我们通过 JavaScript 动态创建的时针、分针和秒针,并直接在 JavaScript 中通过控制行内样式的 transform 属性,设置 rotate 的值,实现指针的旋转。这样的方式,对于 DOM 的控制消耗较大,若放到大项目中,对项目性能具有一定的影响,也让 JavaScript 代码较多,降低了可读性。

而且,设置实时时间的 setRealTime 函数,我们使用了递归调用,每隔一秒钟调用一次,那么每隔一秒钟就会运行整个函数一次,感觉简直是资源的浪费。

仔细想想,都已经是 4202 年了,感觉还是使用 JavaScript 大量控制 DOM 的方式,怎么看都有点点过时了。

曾经,听一位前端大神(尊重隐私,不透露名字)说过一句话:

 那么,秉承这一原则,我们对时钟页面作更加前卫的进阶处理吧!


一、时钟指针静态化

咱们前面是使用 JavaScript 动态生成的三个指针元素,通过 createElement 方法创建 DOM 对性能消耗较大,而且感觉不是必须的。于是我们将其直接写到 html 中:

<div class="dial">
  <div class="pointer hour"></div>
  <div class="pointer minute"></div>
  <div class="pointer second"></div>
</div>

如此一来,原本 JavaScript 中占有 16 行的创建指针的 createPointer 函数就可以删除了。

创建刻度的 createScale 函数不能删除,因为刻度有足足 60 个之多,直接在 html 中写 60 个 span 标签,还要每 5 个就给其加一个值为 bold 的 class 属性。相比之下,感觉还是在 JavaScript 中使用循环生成,代码显得更加简洁一些,逻辑上也更加优雅一些。

删除了创建指针的 createPointer 函数,对应的创建刻度的 createScale 函数的最后一行,就不能再调用创建指针的 createPointer 函数,而是应该直接调用设置实时时间的 setRealTime 函数:

/**
 * @description 创建刻度
*/
const createScale = () => {
  const oDiv = document.querySelector('.dial'); // 获取钟表盘面
  for (let i = 0; i < 60; i++) {
    const oSpan = document.createElement('span'); // 创建刻度元素
    oSpan.style.transform = `rotate(${6 * i}deg)`; // 设置旋转刻度属性
    if (i % 5 === 0) {
      oSpan.className = 'bold'; // 每 5 个刻度,让其显示突出一些
    }
    oDiv.appendChild(oSpan); // 将刻度放到钟表盘面
  }
  setRealTime(); // 设置实时时间
}

注意,设置实时时间的 setRealTime 函数,之前我们是把三个指针元素当作参数传入的,现在不再需要传入参数了。


二、重构设置实时时间函数

还是和“行为篇”一样的思路先构建一个设置实时时间的 setRealTime 函数,并获取到会用到的小时、分钟和秒:

/**
 * @description 设置实时时间
*/
const setRealTime = () => {
  const now = new Date(); // 获取当前时间
  const [hour, minute, second] = [now.getHours(), now.getMinutes(), now.getSeconds()]; // 取出当前小时、分钟和秒
}

我们知道,在 CSS 中,控制一个元素的旋转,都是通过 transform 属性中的 rotate 方法实现的,其语法为:transform: rotate(αdeg); 。

α 为旋转的度数,单位 deg 代表的是角度制。

说白了,就是以 transform-origin 定义的坐标位置为极坐标的原点(未定义该属性,默认为该元素的几何中心,即:transform-origin: 50% 50% 0;),顺时针旋转 α 个度数。

既然拿到了实时时间中的小时、分钟和秒,那么我们就可以根据性质,让其转行成度数,并设置到元素中。 

document.documentElement.style.setProperty('--hour', `${30 * hour}deg`); // 根据当前小时设置小时指针的旋转角度
document.documentElement.style.setProperty('--minute', `${6 * minute}deg`); // 根据当前分钟设置分钟指针的旋转角度
document.documentElement.style.setProperty('--second', `${6 * second}deg`); // 根据当前秒设置秒指针的旋转角度

耶???这是嘛呀???

没错,考虑到我们是把三个指针元素直接写在 html 中的,从心理上来说,我是不愿意再次从 JavaScript 中获取这三个元素的。既然如此,就只好把在 JavaScript 中计算出来的值传到 CSS 中进行处理。于是,CSS4 中的变量登场了:

.dial > .hour {
  top: 125px;
  width: 10px;
  height: 200px;
  transform-origin: center 175px;
  transform: rotate(var(--hour)) ;
}
.dial > .minute {
  top: 25px;
  width: 7.5px;
  height: 300px;
  transform-origin: center 275px;
  transform: rotate(var(--minute));
}
.dial > .second {
  top: 25px;
  width: 2.5px;
  height: 300px;
  transform-origin: center 275px;
  transform: rotate(var(--second));
}

对以上的 JavaScript 代码和 CSS 代码进行观察,相信聪明的您已经发现,我们在 JavaScript 中,通过 document.documentElement.style.setProperty 方法,给 CSS 声明了 --hour、--minute、--second 三个变量,其值为计算之后对应的角度值,还带有单位 deg。然后在 CSS 中对应的地方使用 var() 方法并传入对应的变量名,就可以取到相应的值。

 

这样,我们就获取了一个当前时间显示的效果。大功告成了……………………吗? 

仔细观察就会发现,图中的当前时间是 10:46:11。在现实中,这个时候的时针和分针都不应该正正的指向对应值的刻度,而是会顺时针方向向前偏移对应的比例。 

理想是美好的,现实问题也是赤裸裸的存在的:这样的理想情况,怎么实现呢?

首先,我们单独用一个变量 sStart 记录秒针的当前度数(也叫起始位置):

const sStart = 6 * second; // 设置秒指针的起始位置

分钟有 60 个刻度,那么 sStart / 60 就是分针应该在顺时针方向多偏移的角度。于是我们声明一个记录分针的当前度数的变量 mStart:

const mStart = 6 * minute + sStart / 60; // 设置分钟指针的起始位置

同样的道理,小时有 12 个刻度,那么 mStart / 12 就是时针应该在顺时针方向多偏移的角度。于是我们也声明一个记录时针的当前度数的变量 hStart:

const hStart = 30 * hour + mStart / 12; // 设置小时指针的起始位置

于是,整个设置实时时间的 setRealTime 函数就变成了这样:

/**
 * @description 设置实时时间
*/
const setRealTime = () => {
  const now = new Date(); // 获取当前时间
  const [hour, minute, second] = [now.getHours(), now.getMinutes(), now.getSeconds()]; // 取出当前小时、分钟和秒
  const sStart = 6 * second; // 设置秒指针的起始位置
  const mStart = 6 * minute + sStart / 60; // 设置分钟指针的起始位置
  const hStart = 30 * hour + mStart / 12; // 设置小时指针的起始位置

  document.documentElement.style.setProperty('--hour', `${hStart}deg`); // 根据当前小时设置小时指针的旋转角度
  document.documentElement.style.setProperty('--minute', `${mStart}deg`); // 根据当前分钟设置分钟指针的旋转角度
  document.documentElement.style.setProperty('--second', `${sStart}deg`); // 根据当前秒设置秒指针的旋转角度
}

得到的效果如下:

 

嗯~ o(* ̄▽ ̄*)o~~~~~~无论如何,看时针和分针,确实和现实中的钟表显示一致了。 


三、让时钟动起来

前面“行为篇”中,我们是采用在设置实时时间的 setRealTime 函数中进行递归调用的。尽管我们只需要每隔一秒钟调用一次该函数,但是每次调用都会把上面繁琐的计算都运行一次,资源浪费不说,总是过不了心里的这一关。

设想一下,咱们已经根据当前时间让指针的起始位置都确定了。接下来只需要设置让时针自动每 12 个小时(216000 秒)绕一圈,让分针自动每一个小时(3600 秒)绕一圈,让秒针自动每 60 秒绕一圈,岂不美哉? 

三个指针都是绕一圈后继续重复,需要具备两个条件:

  1. 需要知道每一个指针绕一圈之后的度数,即:起始位置 + 360 deg;

  2. 需要使用 CSS 动画来完成。

那么,我们先在设置实时时间的 setRealTime 函数中计算一下每一个指针绕一圈之后的度数,并继续通过 CSS4 变量传递给 CSS:

document.documentElement.style.setProperty('--toHour', `${hStart + 360}deg`); // 根据当前小时设置小时指针的旋转结束角度
document.documentElement.style.setProperty('--toMinute', `${mStart + 360}deg`); // 根据当前分钟设置分钟指针的旋转结束角度
document.documentElement.style.setProperty('--toSecond', `${sStart + 360}deg`); // 根据当前秒设置秒指针的旋转结束角度

这样,完整的设置实时时间的 setRealTime 函数就是这样的:

/**
 * @description 设置实时时间
*/
const setRealTime = () => {
  const now = new Date(); // 获取当前时间
  const [hour, minute, second] = [now.getHours(), now.getMinutes(), now.getSeconds()]; // 取出当前小时、分钟和秒
  const sStart = 6 * second; // 设置秒指针的起始位置
  const mStart = 6 * minute + sStart / 60; // 设置分钟指针的起始位置
  const hStart = 30 * hour + mStart / 12; // 设置小时指针的起始位置

  document.documentElement.style.setProperty('--hour', `${hStart}deg`); // 根据当前小时设置小时指针的旋转角度
  document.documentElement.style.setProperty('--minute', `${mStart}deg`); // 根据当前分钟设置分钟指针的旋转角度
  document.documentElement.style.setProperty('--second', `${sStart}deg`); // 根据当前秒设置秒指针的旋转角度
  document.documentElement.style.setProperty('--toHour', `${hStart + 360}deg`); // 根据当前小时设置小时指针的旋转结束角度
  document.documentElement.style.setProperty('--toMinute', `${mStart + 360}deg`); // 根据当前分钟设置分钟指针的旋转结束角度
  document.documentElement.style.setProperty('--toSecond', `${sStart + 360}deg`); // 根据当前秒设置秒指针的旋转结束角度
}

传递的 --toHour、--toMinute、--toSecond 变量,需要在 CSS 中定义到动画的最后一帧中。于是我们在 CSS 中分别定义三个指针的旋转动画:

@keyframes hour {
  to {
    transform: rotate(var(--toHour));
  }
}
@keyframes minute {
  to {
    transform: rotate(var(--toMinute));
  }
}
@keyframes second {
  to {
    transform: rotate(var(--toSecond));
  }
}

 然后,在时针、分针、秒针元素对应的 CSS 中分别调用对应的动画:

.dial > .hour {
  top: 125px;
  width: 10px;
  height: 200px;
  transform-origin: center 175px;
  transform: rotate(var(--hour)) ;
  animation: hour 216000s linear infinite;
}
.dial > .minute {
  top: 25px;
  width: 7.5px;
  height: 300px;
  transform-origin: center 275px;
  transform: rotate(var(--minute));
  animation: minute 3600s linear infinite;
}
.dial > .second {
  top: 25px;
  width: 2.5px;
  height: 300px;
  transform-origin: center 275px;
  transform: rotate(var(--second));
  animation: second 60s linear infinite;
}

 注意:三个动画在调用的时候都设置了匀速、重复;三个动画对应的时间都是秒数。

看一下效果:

 

这样,我们就用相对理想的方式把我们的时钟给完成了。 


完整源代码

最后,还是把最终的完整源代码贡献出来,大家拿走不谢( ̄_, ̄ ) 

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Clock</title>
<style type="text/css">
body {
  background: #333;
}
.dial {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  width: 600px;
  height: 600px;
  margin: auto;
  border: 5px solid #fff;
  border-radius: 50%;
}
.dial > span {
  position: absolute;
  top: 0;
  right: 0;
  left: 0;
  display: block;
  width: 5px;
  height: 10px;
  margin: auto;
  background: #fff;
  transform-origin: center 300px;
}
.dial > span.bold {
  width: 7.5px;
  height: 20px;
}
.dial > .pointer {
  position: absolute;
  right: 0;
  left: 0;
  margin: auto;
  background: #fff;
  border-radius: 5px;
}
.dial > .hour {
  top: 125px;
  width: 10px;
  height: 200px;
  transform-origin: center 175px;
  transform: rotate(var(--hour)) ;
  animation: hour 216000s linear infinite;
}
.dial > .minute {
  top: 25px;
  width: 7.5px;
  height: 300px;
  transform-origin: center 275px;
  transform: rotate(var(--minute));
  animation: minute 3600s linear infinite;
}
.dial > .second {
  top: 25px;
  width: 2.5px;
  height: 300px;
  transform-origin: center 275px;
  transform: rotate(var(--second));
  animation: second 60s linear infinite;
}
@keyframes hour {
  to {
    transform: rotate(var(--toHour));
  }
}
@keyframes minute {
  to {
    transform: rotate(var(--toMinute));
  }
}
@keyframes second {
  to {
    transform: rotate(var(--toSecond));
  }
}
</style>
<script type="text/javascript">
/**
 * @description 创建刻度
*/
const createScale = () => {
  const oDiv = document.querySelector('.dial'); // 获取钟表盘面
  for (let i = 0; i < 60; i++) {
    const oSpan = document.createElement('span'); // 创建刻度元素
    oSpan.style.transform = `rotate(${6 * i}deg)`; // 设置旋转刻度属性
    if (i % 5 === 0) {
      oSpan.className = 'bold'; // 每 5 个刻度,让其显示突出一些
    }
    oDiv.appendChild(oSpan); // 将刻度放到钟表盘面
  }
  setRealTime(); // 设置实时时间
}

/**
 * @description 设置实时时间
*/
const setRealTime = () => {
  const now = new Date(); // 获取当前时间
  const [hour, minute, second] = [now.getHours(), now.getMinutes(), now.getSeconds()]; // 取出当前小时、分钟和秒
  const sStart = 6 * second; // 设置秒指针的起始位置
  const mStart = 6 * minute + sStart / 60; // 设置分钟指针的起始位置
  const hStart = 30 * hour + mStart / 12; // 设置小时指针的起始位置

  document.documentElement.style.setProperty('--hour', `${hStart}deg`); // 根据当前小时设置小时指针的旋转角度
  document.documentElement.style.setProperty('--minute', `${mStart}deg`); // 根据当前分钟设置分钟指针的旋转角度
  document.documentElement.style.setProperty('--second', `${sStart}deg`); // 根据当前秒设置秒指针的旋转角度
  document.documentElement.style.setProperty('--toHour', `${hStart + 360}deg`); // 根据当前小时设置小时指针的旋转结束角度
  document.documentElement.style.setProperty('--toMinute', `${mStart + 360}deg`); // 根据当前分钟设置分钟指针的旋转结束角度
  document.documentElement.style.setProperty('--toSecond', `${sStart + 360}deg`); // 根据当前秒设置秒指针的旋转结束角度
}

window.onload = createScale;
</script>
</head>
<body>
  <div class="dial">
    <div class="pointer hour"></div>
    <div class="pointer minute"></div>
    <div class="pointer second"></div>
  </div>
</body>
</html>

篇幅有限,很多地方都没有过多展开讲解了。之后,我们会写出更多有趣的文章,大家敬请期待!

 

关注我,为您奉上更多精彩内容! 

标签:写个,const,--,transform,进阶篇,deg,设置,时钟,指针
From: https://blog.csdn.net/Conan9912345/article/details/140082060

相关文章

  • Python速成指南:进阶篇
    前言欢迎来到Python速成指南的进阶篇。如果你已经完成了基础篇的学习(Python速成指南:从零开始的编程之旅-CSDN博客),并且对Python的基本概念有了扎实的理解,那么你已经为进入更深层次的Python世界做好了准备。在这个进阶篇中,我们将深入探讨Python的高级特性,并着重于如何在实际工......
  • 使用pyqt5编写一个七彩时钟
    使用pyqt5编写一个七彩时钟效果代码解析定义RainbowClockWindow类初始化用户界面显示时间方法完整代码在这篇博客中,我们将使用PyQt5创建一个简单的七彩数字时钟。效果代码解析定义RainbowClockWindow类classRainbowClockWindow(QMainWindow):def......
  • verilog写12 小时时钟(带上午/下午指示器)计数器(HDLbits Count clock)
    Createasetofcounterssuitableforuseasa12-hourclock(witham/pmindicator).Yourcountersareclockedbyafast-running clk,withapulseon ena wheneveryourclockshouldincrement(i.e.,oncepersecond).reset resetstheclockto12:00AM.......
  • 桌面时钟APP的简单开发(Android开发)
    开发目的想打造个性化的私人闹钟APP,放到桌面上提示时间,但是感觉应用商店中的相关软件不好用,有些有广告,就难受。而且没有办法DIY自己想要的时钟样式。所以,开搞!(初学者入门,慢慢摸索呗)开发环境1、windows操作系统2、Android Studio20243、JDK1.8(已配置的jdk环境,因为Andro......
  • STM32基础篇--复位和时钟控制RCC
    1.时钟树1.1时钟问:什么是时钟?为什么要有时钟?时钟是怎么产生的?(1)什么是时钟?时钟就是具有周期性的脉冲信号,相当于单片机的心脏,给单片机提供一个统一的信号,要想使用单片机的外设必须开启相应的时钟。对CPU来说,假设CPU在一个时钟周期内执行一条指令,若时钟频率越高,则时钟周期......
  • 【Power Compiler手册】9.时钟门控(3时钟门控风格)
    时钟门控风格PowerCompiler工具根据您指定的风格在设计中插入时钟门控单元。当没有指定时钟门控风格时,工具会使用一组预定义的风格用于时钟门控。set_clock_gating_style命令的默认设置适用于大多数设计。以下部分将详细讨论默认时钟门控风格和使用特定时钟门控风格:•......
  • 【Power Compiler手册】9.时钟门控(2)
    指定时钟门控延迟在综合过程中,DesignCompiler假设时钟是理想的。理想时钟在时钟网络中不产生任何延迟。这种假设是因为直到时钟树综合之后,实际的时钟网络延迟才为人所知。实际上,时钟并不是理想的,并且通过时钟网络存在非零延迟。对于具有时钟门控的设计,寄存器处的时钟网络延......
  • 进阶篇06——锁
    概述全局锁表级锁表锁元数据锁元数据锁是系统自动加的,不需要我们手动执行命令添加。意向锁 意向锁和元数据锁一样,也是在加行锁的时候自动给表加上相应的意向锁,不需要我们手动添加。行级锁行锁读锁和读锁兼容,写锁和读锁互斥,写锁和写锁也互斥间隙锁、临......
  • 【Power Compiler手册】9.时钟门控(1)
    在更高层次上的功耗优化对最终门级设计的功耗降低有显著影响。时钟门控是降低设计功耗的重要技术。有关PowerCompiler工具中时钟门控的信息,请参阅以下主题:-时钟门控简介-使用时钟门控条件-插入时钟门控-时钟门控流程-指定时钟门控延迟-从时钟门控单元到寄存......
  • nalog_clock 时钟
    analog_clock:^0.1.0#时钟 classMyApp1extendsStatelessWidget{@overrideWidgetbuild(BuildContextcontext){//获取当前北京时间returnCenter(child:AnalogClock(//时钟的装饰,指定边框、背景色和形状decoration:BoxDecorat......