『 实战』从0到1,带你开发开发一款令人惊叹的时钟特效

1,262 阅读10分钟

我正在参加「码上掘金挑战赛」详情请看:码上掘金挑战赛来了!

前言

我们在最初学 js 的时候,经常会遇到 Date 对象,它主要用于获取一些日期和时间相关的数据,包括当前的时分秒、年月日等等,而我们常常使用 Date 练手的项目就是开发一个简单的时钟或者获取当前年月日的组件,今天给大家带来一款效果令人惊叹的时钟,让我们先来看一下相关的效果,如下:

demo1.gif

通过上述的动态图,我们可以看到这个时钟特效包含两个部分,上面是基础的时钟特效,下面是一个动态时间,虽然这个效果看起来不难,但是它的细节还是有很多的,下面我们就一起来学习一下吧!

表盘

首先我们将表盘画出来,这里主要用到了 CSS 中的 border-radius 属性,并且我们要将表盘的指针的起点固定在正中心,就需要用到 CSS 中的 flex 布局,关于 flex 布局,引用 MDN 的话来说:

Flexible Box 模型,通常被称为 flexbox,是一种一维的布局模型。它给 flexbox 的子元素之间提供了强大的空间分布和对齐能力。

由于 flex 布局是一维的布局模型,因此我们可以通过 flex-direction 改变元素的排列方式,flex-direction 的默认值是 row,意思是按行(即X轴排列),我们可以通过修改 rowcolumn 来改变它的排列方式为列(即Y轴排列),并且根据排列方式的不同,我们可以设置行或列中元素的位置,常见的属性有 align-itemsjustify-content,这里我们需要将整个表盘居中显示,就需要设置这两个属性的值为 center,下面我们一起来看一下表盘的实现。

表盘

首先画出最外层的表盘,相关 html 代码如下:

<div class="container">
    <div class="clock">
        <span><b>1</b></span>
        <span><b>2</b></span>
        <span><b>3</b></span>
        <span><b>4</b></span>
        <span><b>5</b></span>
        <span><b>6</b></span>
        <span><b>7</b></span>
        <span><b>8</b></span>
        <span><b>9</b></span>
        <span><b>10</b></span>
        <span><b>11</b></span>
        <span><b>12</b></span>
    </div>
</div>

首先我们需要将 container 进行居中,并且给它设置一个背景色,然后添加相关的阴影。在 CSS 中,我们可以通过 box-shadow 来给一个元素添加阴影,box-shadow 有多个参数,这里借助 MDN 官方的介绍,如下:

  • 当给出两个、三个或四个 <length>值时。
    • 如果只给出两个值,那么这两个值将会被当作 <offset-x><offset-y> 来解释。
    • 如果给出了第三个值,那么第三个值将会被当作<blur-radius>解释。
    • 如果给出了第四个值,那么第四个值将会被当作<spread-radius>来解释。
  • 可选,inset关键字。
  • 可选,<color>值。

上面的介绍是什么意思呢?别急,我们写几个例子就明白了。

box-shadow 有两个参数时:box-shadow: 10px 10px; 这两个值就分别代表 X轴Y轴 方向的阴影渲染偏移,默认颜色是白色,如图所示:

image.png

box-shadow 有三个参数时:box-shadow: 10px 10px 10px; 最后一个值表示阴影模糊半径,如下图所示:

image.png

box-shadow 有四个参数时:box-shadow: 10px 10px 10px 10px; 最后一个值表示阴影扩散半径,如下图所示:

image.png

因为上面的示例中我们没有添加 color 选项,因此默认是阴影是白色的。我们可以通过添加相关的颜色值来改变阴影的颜色,这里就不做演示了。

我们继续回到 container 中来,通过添加如下样式,就能搭建一个基础的表盘,代码如下:

//less

.container {
    position: relative;
    background: #2f363e;
    border-radius: 20px;
    border-top-left-radius: 225px;
    border-top-right-radius: 225px;
    // 通过 box-shadow 设置表盘的阴影,而 box-shadow 属性是可以叠加的,多个阴影通过逗号进行分割
    box-shadow: 25px 25px 75px rgba(0, 0, 0, .75),
    10px 10px 70px rgba(0, 0, 0, .25),
    inset 5px 5px 20px rgba(255, 255, 255, .2),
    inset -5px -5px 15px rgba(0, 0, 0, .75);
    // 通过 flex 布局将整个表盘展示在页面正中间
    display: flex;
    justify-content: center;
    align-items: center;
    
    .clock {
        position: relative;
        width: 450px;
        height: 450px;
        background: #2f363e;
        border-radius: 50%;
        box-shadow: 10px 50px 70px rgba(0, 0, 0, .25),
        inset 5px 5px 10px rgba(0, 0, 0, .5),
        inset 5px 5px 20px rgba(255, 255, 255, .2),
        inset -5px -5px 15px rgba(0, 0, 0, .75);
        display: flex;
        justify-content: center;
        align-items: center;
        margin-bottom: 30px;
    }
}

通过设置表盘的基础样式,我们可以得到如下的一个基础表盘:

image.png

在上面的代码中,因为使用了 box-shadow 属性,并且叠加了四层阴影,因此让整个表盘看起来就像是一个 3D 的表盘,我们在日常的开发中就可以通过多次叠加 box-shadow 属性值从而来得到一个令人满意的阴影效果。

我们在上面看到表盘的数字还没有样式,都挤在中间的区域,之所以挤在中间,是因为我们前面介绍过使用 flex 布局默认的布局方式就是 row,并且设置了 justify-contentalign-items 的值为 center,因此所有的数字都挤在中间,并且是横向沿着 X轴 排列的,这就是 flex 最基础的布局。

下面我们让整个表盘的数字按照正常的刻度来展示,在添加数字刻度之前,我们还需要修改一下相关的 html 代码,因为我们并不知道每个数字所在的位置,一般我们如果用 js 计算,就是用 360 / 12 来获取每个刻度的弧度,然后进行设置,这里我们还是用前面文章中介绍的 var 来进行获取。首先修改相关 html 如下:

<!-- 时钟数值 -->
<span style="--i:1;"><b>1</b></span>
<span style="--i:2;"><b>2</b></span>
<span style="--i:3;"><b>3</b></span>
<span style="--i:4;"><b>4</b></span>
<span style="--i:5;"><b>5</b></span>
<span style="--i:6;"><b>6</b></span>
<span style="--i:7;"><b>7</b></span>
<span style="--i:8;"><b>8</b></span>
<span style="--i:9;"><b>9</b></span>
<span style="--i:10;"><b>10</b></span>
<span style="--i:11;"><b>11</b></span>
<span style="--i:12;"><b>12</b></span>

然后我们继续来写刚才的样式,如下:

// less

span {
    position: absolute;
    inset: 20px;
    color: #fff;
    text-align: center;
    // 在上面说过,我们计算的方式是 360 / 12 ,因此每一个数组的旋转角度就是 30 * 它是第几个
    transform: rotate(calc(30deg * var(--i)));

    b {
        font-size: 2em;
        opacity: .5;
        font-weight: 600;
        display: inline-block;
        // span 内部的文字要朝反方向旋转,不然数字的朝向就会展示错误
        transform: rotate(calc(-30deg * var(--i)));
    }
}

通过上面的修改,我们可以得到如下的效果:

image.png

接下来我们需要画出中间的指针以及相关的表盘轮,修改一下 html,代码如下:

<div class="clock">
    <!-- 时间刻度 -->
    <div class="circle" style="--clr:#04fc43" id="sc"><i></i></div>
    <div class="circle min-circle" style="--clr:#fee800" id="mn"><i></i></div>
    <div class="circle sec-circle" style="--clr:#ff2972" id="hr"><i></i></div>
    
    <!-- 时钟数值 -->
</div>

其中我们在三个 div 中添加了行间的 style,主要用来设置这三个圆的颜色,添加相关的 CSS 样式,代码如下:

.circle {
    position: absolute;
    width: 300px;
    height: 300px;
    border: 2px solid rgba(0, 0, 0, .25);
    border-radius: 50%;
    display: flex;
    justify-content: center;
    align-items: flex-start;
    z-index: 10;

    &.min-circle {
        width: 240px;
        height: 240px;
        z-index: 9;
    }

    &.sec-circle {
        width: 180px;
        height: 180px;
        z-index: 8;
    }

    > i {
        position: absolute;
        width: 6px;
        height: 50%;
        background: var(--clr);
        opacity: .5;
        transform-origin: bottom;
        transform: scaleY(.5);
    }

    &:nth-child(1) {
        i {
            width: 2px;
        }
    }

    &:nth-child(2) {
        i {
            width: 6px;
        }
    }

    &::before {
        content: '';
        position: absolute;
        top: -8.5px;
        // left: 5%;
        width: 15px;
        height: 15px;
        border-radius: 50%;
        background: var(--clr);
        box-shadow: 0 0 20px var(--clr), 0 0 60px var(--clr);
    }
}

在上面的 CSS 代码中,我们只需要注意将每一个圆环里面的 i 标签的变换位置设置为 bottom 即可,这样我们设置元素是缩放时,都会以底部为起点,而不会从中心开始缩放,实现的效果如下图所示:

image.png

这个色彩艳丽的表盘已经有了,接下来还有下面的电子时钟,我们接着盘!

电子时钟

因为表盘和时钟是分开的,因此我们还需要添加时钟相关的 html,代码如下:

<!-- 底部时间 -->
<div class="time">
    <div id="hours" style="--clr:#04fc43">00</div>
    <div id="minutes" style="--clr:#fee800">00</div>
    <div id="seconds" style="--clr:#ff2972">00</div>
    <div id="ampm">AM</div>
</div>

在这个电子时钟里面,我们额外添加了一个当然是上午还是下午的展示,因为我们的时间展示是按12小时制的,因此会区分当前的上午还是下午,接着我们添加一下电子时钟的 CSS 样式,代码如下:

.time {
    margin-bottom: 40px;
    display: flex;
    padding: 10px 20px;
    font-size: 2em;
    font-weight: 600;
    border: 2px solid rgba(0, 0, 0, .5);
    border-radius: 40px;
    box-shadow: 5px 5px 10px rgba(0, 0, 0, .5),
    inset 5px 5px 20px rgba(255, 255, 255, .2),
    inset -5px -5px 15px rgba(0, 0, 0, .75);

    div {
        position: relative;
        width: 60px;
        text-align: center;
        font-weight: 500;
        color: var(--clr);

        &:nth-child(1), &:nth-child(2) {
            &::after {
                content: ':';
                position: absolute;
                right: -4px;
            }
        }

        &:last-child {
            font-size: .5em;
            display: flex;
            justify-content: center;
            align-items: center;
            color: #fff;
        }

        &:nth-child(2) {
            &::after {
                animation: animate 1s steps(1) infinite;
            }
        }
    }
}

@keyframes animate {
    0% {
        opacity: 1;
    }
    50% {
        opacity: 0;
    }
}

在这个 CSS 的最后面,添加了一个动画,这个动画主要用于电子时钟的秒针跳动,我们可以看一下实现的效果,如图:

demo4.gif

样式写到这里,这个炫酷的时钟已经完成了静态的内容,接下来我们就需要让整个时钟能够真正的动起来,并且实时获取到当前的时间。

动态电子时钟

在最开始也介绍过在 js 中我们常用的 Date,这里我们需要借助 Date 来获取当前日期的时分秒,然后将对应的时间展示在时钟里。

关于 Date,我们还是先来看一下 MDN 官方文档,引用官方的介绍如下:

Date 对象则基于 Unix Time Stamp,即自 1970 年 1 月 1 日(UTC)起经过的毫秒数。

创建一个 Date 对象的唯一方法是通过new 操作符,写法如下:

const date = new Date()

创建出来的 date 对象,上面包含很多方法,包括 date.getHoursdate.getMinutesdate.getSeconds 等,当然还有更多的方法,如果对 Date 不熟悉的童鞋,可以点击这里进行查看。

我们只需要借助上述的三个 API 即可获取到当前的时分秒,相关的 js 代码如下:

...other code

constructor() {
    this.hr = document.getElementById('hr');
    this.mn = document.getElementById('mn');
    this.sc = document.getElementById('sc');

    this.hours = document.getElementById('hours');
    this.minutes = document.getElementById('minutes');
    this.seconds = document.getElementById('seconds');
    this.ampm = document.getElementById('ampm');

    this.init();
}

getClock() {
    const day = new Date();
    const hh = day.getHours() * 30;
    const mm = day.getMinutes() * 6;
    const ss = day.getSeconds() * 6;

    this.hr.style.transform = `rotateZ(${hh + (mm / 12)}deg)`;
    this.mn.style.transform = `rotateZ(${mm}deg)`;
    this.sc.style.transform = `rotateZ(${ss}deg)`;
}

...other code

通过获取到当前的时分秒,然后设置对应元素的 transform 属性,即可在时间发生改变后修改当前元素展示的形态,实现的效果如下图所示:

face2.gif

时钟的效果实现了,接下来还剩下底部的电子钟了。电子钟的效果其实也是借助了 Date 对象,使用的方法跟上面的类似,相关 js 代码如下:

...other code

getTimer() {
    let h = new Date().getHours();
    let m = new Date().getMinutes();
    let s = new Date().getSeconds();
    let am = h >= 12 ? 'PM' : 'AM';

    if (h > 12) {
        h = h - 12;
    }

    h = (h < 10) ? '0' + h : h;
    m = (m < 10) ? '0' + m : m;
    s = (s < 10) ? '0' + s : s;

    this.hours.innerHTML = h;
    this.minutes.innerHTML = m;
    this.seconds.innerHTML = s;
    this.ampm.innerHTML = am;
}

...other code

其中需要注意的,也仅仅只是判断当前的时间是上午还是下午,通过获取当前的小时,根据12小时时间制来区分就可以了,最终的实现效果在这里进行查看:

总结

通过学习 js 中的 Date 对象,实现了一个时钟特效,当然 Date 本身还有很多其它的属性,不仅仅可以获取当前的时分秒,还可以获取当然的年月日,并且像一些第三方库,例如:moment.jsdayjs,都是通过 Date 对象来实现相关的时间及日期的获取,因此我们还是有必要掌握 Date 相关的使用的。

最后,如果这篇文章有帮助到你,❤️关注+点赞❤️鼓励一下作者,谢谢大家

往期回顾

CSS 复刻 iPhone14,并接到了优弧的电话😏,还不快接电话?

今年中秋的月亮真漂亮~

『 纯 CSS 实现』哇塞,彩虹🌈还能这么玩?

『 禁止吸烟🚭』纯 CSS 实现 | 禁烟也能做的这么酷炫

送你一个可爱的大圆脸😁,速来~

这么炫酷的 3D Menu 效果,真的不来看看?

产品经理:你这个效果不行,我想要一个五彩斑斓的黑!我:。。。