用 CSS 三角函数画钟表
·
Yin灏
前言
目前主流浏览器都已经支持 CSS 三角函数了,今天我们就用三角函数来画一个时钟。
表盘
先来看一下表盘,它由一个底盘和 12 个刻度组成,刻度用 time 标签来表示:
<div class="clock">
<div class="clock-face">
<time datetime="12:00">12</time>
<time datetime="1:00">1</time>
<time datetime="2:00">2</time>
<time datetime="3:00">3</time>
<time datetime="4:00">4</time>
<time datetime="5:00">5</time>
<time datetime="6:00">6</time>
<time datetime="7:00">7</time>
<time datetime="8:00">8</time>
<time datetime="9:00">9</time>
<time datetime="10:00">10</time>
<time datetime="11:00">11</time>
</div>
</div>
表盘是圆形橙色的,并且我们定义了刻度标签的宽度、钟表宽度和表盘半径,也会在后面用到:
.clock {
/* 刻度宽度 */
--scale-w: 20px;
/* 钟表宽度(或直径),clamp(min, val, max) */
--w: clamp(5rem, 400px, 40rem);
/* 表盘半径 */
--r: calc(var(--w)/2);
/* 长宽比 */
aspect-ratio: 1;
background-color: tomato;
border-radius: 50%;
height: var(--w);
position: relative;
}
下一步来安排刻度的位置,一圈 360 度,12 个刻度,所以刻度与刻度之间是 30 度。
先来指定每个刻度对应的角度:
.clock time:nth-child(1) { --d: 270deg; }
.clock time:nth-child(2) { --d: 300deg; }
.clock time:nth-child(3) { --d: 330deg; }
.clock time:nth-child(4) { --d: 0deg; }
.clock time:nth-child(5) { --d: 30deg; }
.clock time:nth-child(6) { --d: 60deg; }
.clock time:nth-child(7) { --d: 90deg; }
.clock time:nth-child(8) { --d: 120deg; }
.clock time:nth-child(9) { --d: 150deg; }
.clock time:nth-child(10) { --d: 180deg; }
.clock time:nth-child(11) { --d: 210deg; }
.clock time:nth-child(12) { --d: 240deg; }
有了角度后,就该计算刻度的具体位置了:
.clock time {
--x: calc(var(--r) + var(--r) * cos(var(--d)));
--y: calc(var(--r) + var(--r) * sin(var(--d)));
position: absolute;
top: var(--y);
left: var(--x);
width: var(--scale-w);
aspect-ratio: 1;
display: flex;
align-items: center;
justify-content: center;
background: orange;
}
此时的刻度有点偏,我们把表盘半径调整一下:
.clock {
/* ... */
/* 钟表半径 */
--r: calc((var(--w) - var(--scale-w)) / 2);
/* ... */
}
指针
在表盘的 html 下面加上指针:
<div class="clock">
<div class="clock-face">...</div>
<span class="arm seconds"></span>
<span class="arm minutes"></span>
<span class="arm hours"></span>
<span class="arm center"></span>
</div>
再来定义指针的样式。其中** –arm-bg** 表示指针背景色,--arm-w 表示指针宽度,--arm-h 表示指针高度,**–arm-init-deg **表示指针初始时的角度:
.arm {
background-color: var(--arm-bg);
border-radius: calc(var(--arm-w) * 2);
display: block;
height: var(--arm-h);
left: calc((var(--w) - var(--arm-w)) / 2);
position: absolute;
top: calc((var(--w) / 2) - var(--arm-h));
transform: rotate(var(--arm-init-deg, 0deg));
transform-origin: bottom;
width: var(--arm-w);
}
定义时分秒针的样式:
.seconds {
--arm-bg: hsl(0, 5%, 40%);
--arm-h: 145px;
--arm-w: 2px;
}
.minutes {
--arm-bg: #333;
--arm-h: 145px;
--arm-w: 6px;
--arm-init-deg: 20deg;
}
.hours {
--arm-bg: #333;
--arm-h: 110px;
--arm-w: 6px;
--arm-init-deg: 50deg;
}
来看下效果:
动画
接下来给指针加上动画,动画比较简单,其实就是旋转一圈:
@keyframes turn {
to {
transform: rotate(1turn);
}
}
给每个指针都加上动画,不过动画时长和步长需要根据指针类型调整:
.seconds {
/* ... */
animation: turn 60s steps(60, end) infinite;
}
.minutes {
/* ... */
animation: turn 3600s steps(60, end) infinite;
}
.hours {
/* ... */
animation: turn 43200s linear infinite;
}
去掉刻度的背景后,再来看下效果,是不是还不错:
附完整代码:
HTML
<div class="clock">
<div class="clock-face">
<time datetime="12:00">12</time>
<time datetime="1:00">1</time>
<time datetime="2:00">2</time>
<time datetime="3:00">3</time>
<time datetime="4:00">4</time>
<time datetime="5:00">5</time>
<time datetime="6:00">6</time>
<time datetime="7:00">7</time>
<time datetime="8:00">8</time>
<time datetime="9:00">9</time>
<time datetime="10:00">10</time>
<time datetime="11:00">11</time>
</div>
<span class="arm seconds"></span>
<span class="arm minutes"></span>
<span class="arm hours"></span>
<span class="arm center"></span>
</div>
CSS
.clock {
margin-left:400px;
/* 刻度宽度 */
--scale-w: 20px;
/* 钟表宽度(或直径) */
--w: clamp(5rem, 400px, 40rem);
/* 钟表半径 */
--r: calc((var(--w) - var(--scale-w)) /2);
aspect-ratio: 1;
background-color: tomato;
border-radius: 50%;
height: var(--w);
position: relative;
}
.clock time:nth-child(1) { --d: 270deg; }
.clock time:nth-child(2) { --d: 300deg; }
.clock time:nth-child(3) { --d: 330deg; }
.clock time:nth-child(4) { --d: 0deg; }
.clock time:nth-child(5) { --d: 30deg; }
.clock time:nth-child(6) { --d: 60deg; }
.clock time:nth-child(7) { --d: 90deg; }
.clock time:nth-child(8) { --d: 120deg; }
.clock time:nth-child(9) { --d: 150deg; }
.clock time:nth-child(10) { --d: 180deg; }
.clock time:nth-child(11) { --d: 210deg; }
.clock time:nth-child(12) { --d: 240deg; }
.clock time {
--x: calc(var(--r) + var(--r) * cos(var(--d)));
--y: calc(var(--r) + var(--r) * sin(var(--d)));
position: absolute;
top: var(--y);
left: var(--x);
width: var(--scale-w);
aspect-ratio: 1;
display: flex;
align-items: center;
justify-content: center;
}
.arm {
background-color: var(--arm-bg);
border-radius: calc(var(--arm-w) * 2);
display: block;
height: var(--arm-h);
left: calc((var(--w) - var(--arm-w)) / 2);
position: absolute;
top: calc((var(--w) / 2) - var(--arm-h));
transform: rotate(var(--arm-init-deg, 0deg));
transform-origin: bottom;
width: var(--arm-w);
}
@keyframes turn {
to {
transform: rotate(1turn);
}
}
.seconds {
--arm-bg: hsl(0, 5%, 40%);
--arm-h: 145px;
--arm-w: 2px;
animation: turn 60s steps(60, end) infinite;
}
.minutes {
--arm-bg: #333;
--arm-h: 145px;
--arm-w: 6px;
--arm-init-deg: 20deg;
animation: turn 3600s steps(60, end) infinite;
}
.hours {
--arm-bg: #333;
--arm-h: 110px;
--arm-w: 6px;
--arm-init-deg: 50deg;
animation: turn 43200s linear infinite;
}