注册
web

⏰⏰ 手把手实现一个进度条时钟,麻麻再也不用担心我把时间看茬了!


前言


挂钟大家都知道吧,它通过时针、分针和秒针来表示时间,想当初小学刚开始教怎么看时钟的完全看不懂。今天带大家一步步实现一个类进度条时钟的效果,更直观的知晓当前的时间。


本文将会带大家学到以下知识点:



  1. 垂直水平居中方式
  2. gap 属性搭配 flex 布局 实现等边距
  3. Date 日期函数的使用及注意点
  4. CSS 变量的简单应用
  5. svgcircle 标签的用法
  6. stroke-dashoffset 属性和 stroke-dasharray 属性能够的用法

样式重置


首先老规矩,我们将 CSS 样式重置 ,方便各个浏览器统一展示效果。


* {
margin: 0;
padding: 0;
box-sizing: border-box;
}

背景调整


接下来通过添加 min-height: 100vh 属性,将 body 限制为 视口大小 且通过 overflow: hidden 来将 超出部分隐藏


body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #2f363e;
}

这里为了将我们的时钟在屏幕中间展示,我们需要使用 flex 布局body 设置为 水平垂直居中 。同样的,小伙伴们还可以使用 light-heighttransform 等手段实现。


时间绘制


接下来我们要准备 4 个 div ,用来作为展示时间的容器。


<body>
<div id="time">
<div class="circle">
<div id="hours">00</div>
</div>
<div class="circle">
<div id="minutes">00</div>
</div>
<div class="circle">
<div id="seconds">00</div>
</div>
<div class="ap">
<div id="ampm">00</div>
</div>
</div>

</body>

然后给其宽高。


#time {
display: flex;
color: #fff;
}
#time .circle {
position: relative;
width: 150px;
height: 150px;
}

细心的小伙伴一定注意到了这段 CSS 中有个比较特别的属性:gap。这有什么用呢?


我们来看看 MDN 对 gap 属性的描述:



CSS gap 属性是用来设置网格行与列之间的间隙(gutters),该属性是 row-gap 和 column-gap 的简写形式。



gap 属性它适用于 multi-column elements, flex containers, grid containers,也就是多列布局、弹性布局以及网格布局中(知识点++)。因此这里我们用 flex 布局 搭配 gap 是完全行得通的。


我们看看此时的效果如何:


微信截图_20221011180851.png


我们发现最后一个用来表示上午下午的字体有点大,我们将其调小,同时通过 translateY 属性将该元素偏移 -20px(负数表示向上偏移,正数表示向下偏移),和时间做区分。


#time .ap {
position: relative;
font-size: 1em;
transform: translateY(-20px);
}

微信截图_20221011182321.png


这样美观了许多,主次分明。


动态时间


接下来我们要让时间变成实时的,因此我们要用到 Javascript 的 Date 函数了。



  1. 通过 getHours() 获取当前小时数。
  2. 通过 getMinutes() 获取当前分钟数。
  3. 通过 getSeconds() 获取当前秒钟数。

这里有个注意点,通过以上三个方法获取的时间值,都是不带前缀 0 的,并且是 number 类型。什么意思呢?


比如现在是下午的 14:07:04 ,通过 getMinutes() 获取的分钟数是 7 而不是 07,同理,通过 getSeconds()getHours() 获取的时间也是如此。


因此为了美观,我们需要手动给 10 以内 的时间值 补一个 0。具体怎么做呢?这里我用到了字符类型的 padStart 方法,它传递两个参数,分别是数字最后要填充到这个指定的位数,以及用来填充的字符。


为了将 7 变成 07,我们需要将 number 类型的 7 变为字符串类型的 '7',然后执行 '7'.padStart(2, 0)。


除此之外,对于 AM 以及 PM 的区分,我们通过判断 getHours() 的返回值是否大等于 12。如果大于则为 PM,否则是 AM。


处理完数据之后通过修改 innerHTML 的值来改变页面上展现的时间,同时通过 setInterval 来不断执行该操作,实现实时更新时间的效果。


<script>
let hours = document.querySelector('#hours');
let minutes = document.querySelector('#minutes');
let seconds = document.querySelector('#seconds');
let ampm = document.querySelector('#ampm');

setInterval(() => {
let h = `${new Date().getHours() % 12}`.padStart(2, 0);
let m = `${new Date().getMinutes()}`.padStart(2, 0);
let s = `${new Date().getSeconds()}`.padStart(2, 0);
let am = h >= 12 ? 'PM' : 'AM';

hours.innerHTML = h + '\n<div class="tip">HOURS</div>';
minutes.innerHTML = m + '\n<div class="tip">MINUTES</div>';
seconds.innerHTML = s + '\n<div class="tip">SECONDS</div>';
ampm.innerHTML = am;
}, 1000);
</script>

注意点小结:



  1. getHours、getMinutes 以及 getSeconds 返回 number 类型的值(不带前缀 0)
  2. padStart 是字符类型的方法,注意先将类型转为 string 再进行调用。

我们来看看效果:


20221011_190750.gif


画圆


接下来我们要对每个时间的容器都画一个圆的效果,这里我使用了 svg 标签。原因下文会提。


有的小伙伴可能对 svg 标签比较陌生,确实平时开发的时候用的比较少,实际上它和我们普通的标签差不多,而且也能用过 CSS 设置它的一些属性。


这里画圆我们需要用到 circle 标签,其中,cxcy 属性共同确定了一个圆心的位置,r 属性表示待绘制圆的半径。


<div id="time">
<div class="circle" style="--clr: #ff2972">
<svg>
<circle cx="70" cy="70" r="70"></circle>
</svg>
<div id="hours">00</div>
</div>

<div class="circle" style="--clr: #fee800">
<svg>
<circle cx="70" cy="70" r="70"></circle>
</svg>
<div id="minutes">00</div>
</div>

<div class="circle" style="--clr: #04fc43">
<svg>
<circle cx="70" cy="70" r="70"></circle>
</svg>
<div id="seconds">00</div>
</div>

</div>

微信截图_20221011192308.png


可以看到默认情况下,circle 标签的背景色是黑色的,我们给它点样式。


#time .circle svg circle {
width: 100%;
height: 100%;
fill: transparent;
stroke: #191919;
stroke-width: 4;
}

fill 属性表示当前填充 circle 标签应当用什么颜色(实际上就是背景色的意思)。


stroke 属性表示绘制一个边线(实际上就是边框)。


stroke-width 属性一般搭配 stroke 属性一起用,表示边线的宽度。


微信截图_20221011192621.png


经过这么一手修改后已经有模有样了,但是别急,最麻烦的部分要来了。接下来我们要模拟时间进度条了。


模拟进度条


这里我们模拟进度条需要在每个 svg 标签下再添加一个 circle 标签。


<div id="time">
<div class="circle" style="--clr: #ff2972">
<svg>
<circle cx="70" cy="70" r="70"></circle>
<circle cx="70" cy="70" r="70" id="hh"></circle>
</svg>
<div id="hours">00</div>
</div>

<div class="circle" style="--clr: #fee800">
<svg>
<circle cx="70" cy="70" r="70"></circle>
<circle cx="70" cy="70" r="70" id="mm"></circle>
</svg>
<div id="minutes">00</div>
</div>

<div class="circle" style="--clr: #04fc43">
<svg>
<circle cx="70" cy="70" r="70"></circle>
<circle cx="70" cy="70" r="70" id="ss"></circle>
</svg>
<div id="seconds">00</div>
</div>

</div>

同时,通过 CSS 变量给这个新增的 circle 标签各不相同的颜色。


#time .circle svg circle:nth-child(2) {
stroke: var(--clr);
}

此时效果如下:


微信截图_20221011193127.png


接下来我们要用到一个新的属性:stroke-dasharray


我们来看看 MDN 对 stroke-dasharray 属性的介绍:



属性 stroke-dasharray 可控制用来描边的点划线的图案范式。



是不是有点懵,我们看看下面这张动图:


20221011_193806.gif


这是 stroke-dasharray 属性值从 0 开始不断增加的效果。那我们就知道了,实际上这个属性就是控制点划线的长度用的。


那它和我们的进度条有什么关系呢?


不要急,要实现我们的进度条,还需要另一个属性:stroke-dashoffset


我们来看看 MDN 对 stroke-dashoffset 属性的介绍:



属性 stroke-dashoffset 指定了 dash 模式到路径开始的距离。



话不多说,上图:


20221011_194553.gif


这是 stroke-dashoffset 属性值从 0 开始不断增加的效果,是不是很像进度条在跑?


我们就是通过 Javascript 动态修改 stroke-dashoffset 来达到进度条跟着时间一起跑的效果!


let hh = document.querySelector('#hh');
let mm = document.querySelector('#mm');
let ss = document.querySelector('#ss');

setInterval(() => {
...
hh.style.strokeDashoffset = 440 - (440 * h) / 12;
mm.style.strokeDashoffset = 440 - (440 * m) / 60;
ss.style.strokeDashoffset = 440 - (440 * s) / 60;
}, 1000);

20221011_195356.gif


码上掘金


SumXiMRX - 码上掘金 (juejin.cn)


Github 源码地址


juejin-demo/digital-clock-demo at main · catwatermelon/juejin-demo (github.com)


结束语


本文就到此结束了,希望大家共同努力,早日拿下 CSS 💪💪。


如果文中有不对的地方,或是大家有不同的见解,欢迎指出 🙏🙏。


如果大家觉得所有收获,欢迎一键三连💕💕。


作者:CatWatermelon
来源:juejin.cn/post/7153836297218424863

0 个评论

要回复文章请先登录注册