新聞中心
練習(xí) CSS 一個(gè)很好的方法就是繪制各式各樣的 UI,比如這樣一個(gè)時(shí)鐘?

為松北等地區(qū)用戶(hù)提供了全套網(wǎng)頁(yè)設(shè)計(jì)制作服務(wù),及松北網(wǎng)站建設(shè)行業(yè)解決方案。主營(yíng)業(yè)務(wù)為成都網(wǎng)站建設(shè)、成都網(wǎng)站設(shè)計(jì)、松北網(wǎng)站設(shè)計(jì),以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專(zhuān)業(yè)、用心的態(tài)度為用戶(hù)提供真誠(chéng)的服務(wù)。我們深信只要達(dá)到每一位用戶(hù)的要求,就會(huì)得到認(rèn)可,從而選擇與我們長(zhǎng)期合作。這樣,我們也可以走得更遠(yuǎn)!
你也可以訪問(wèn)「文章底部原文鏈接」查看實(shí)際效果。
CSS 繪制這樣一個(gè)布局有幾個(gè)難點(diǎn):
- 環(huán)形排列的刻度。
- 環(huán)形分布的數(shù)字。
- 自動(dòng)運(yùn)行的指針。
下面就來(lái)一一實(shí)現(xiàn)它,相信能學(xué)到很多 CSS 繪制和動(dòng)畫(huà)的小技巧。
一、環(huán)形排列的刻度
提到“環(huán)形”,可以想到錐形漸變 conic-gradient[2]。假設(shè)有這樣一個(gè)容器。
加上一點(diǎn)錐形漸變。
clock{
width: 300px;
height: 300px;
background: conic-gradient(#333 0 15deg, #cddc39 0deg 30deg);
}
可以得到這樣的效果。
如何做出交錯(cuò)相間的效果呢?可以試試 repeating-conic-gradient[3]。
clock{
/**/
background: repeating-conic-gradient(#333 0 15deg, #cddc39 0deg 30deg);
}
效果如下:
還是看不出和刻度有啥關(guān)系?沒(méi)關(guān)系,我們把黑色部分的角度改小一點(diǎn)。
clock{
/**/
background: repeating-conic-gradient(#333 0 1deg, #cddc39 0deg 30deg);
}
效果如下:
這樣繪制出來(lái)的幾條線是不是剛好可以對(duì)應(yīng)時(shí)鐘的刻度?
然后將整個(gè)形狀變成圓環(huán),可以用 MASK 來(lái)實(shí)現(xiàn),實(shí)現(xiàn)如下:
clock{
/**/
border-radius: 50%;
-webkit-mask: radial-gradient(transparent 145px, red 0);
}
效果如下:
其實(shí),這里還有一個(gè)小細(xì)節(jié),黑色部分并不是居中的,需要修正一下(可以更改起始角度,指定 from)。然后,將這個(gè)草綠色換成透明就可以了,完整代碼如下:
clock{
/**/
background: repeating-conic-gradient(from -.5deg, #333 0 1deg, transparent 0deg 30deg);
border-radius: 50%;
-webkit-mask: radial-gradient(transparent 145px, red 0);
}
最終效果:
分鐘的刻度也是同樣的道理,因?yàn)楣灿?60 個(gè)刻度,所以最小角度是 6 度(360 / 60),實(shí)現(xiàn)如下:
clock{
/**/
background: repeating-conic-gradient(#333 0 1deg, #cddc39 0deg 6deg);
}
利用 CSS 背景可以無(wú)限疊加的特性,可以將這兩個(gè)背景繪制在同一個(gè)元素下,所以完整代碼如下:
clock{
/**/
background: repeating-conic-gradient(from -.5deg, #333 0 1deg, transparent 0deg 30deg),
repeating-conic-gradient(from -.5deg, #ccc 0 1deg, transparent 0deg 6deg);
border-radius: 50%;
-webkit-mask: radial-gradient(transparent 145px, red 0);
}
最終表盤(pán)刻度效果如下:
二、環(huán)形分布的數(shù)字
看到這種布局,我的第一反應(yīng)其實(shí)是 textPath[4],這個(gè) SVG 元素可以讓文本沿著指定路徑進(jìn)行排列,比如下面這個(gè) MDN 上的例子:
效果如下:
但是,這種方式有一個(gè)缺陷,無(wú)法改變文字的角度,只能沿著路徑垂直方向,而時(shí)鐘的數(shù)字方向都是正常的。
經(jīng)過(guò)一番琢磨,發(fā)現(xiàn)還有一種方式也有類(lèi)似沿著路徑的布局方式,那就是 offset-path[5]! 下面是 MDN 上的一個(gè)演示效果:
那么和環(huán)形排列數(shù)字有什么關(guān)系呢?假設(shè)有這樣一個(gè)布局:
1
然后將這個(gè)數(shù)字指定到一個(gè)圓形的路徑上(目前僅支持 path )。
num{
offset-path: path('M250 125c0 69.036-55.964 125-125 125S0 194.036 0 125 55.964 0 125 0s125 55.964 125 125z');
}
效果如下(起始點(diǎn)是跟隨 path 路徑的):
然后,可以通過(guò) offset-distance[6]來(lái)改變?cè)卦诼窂缴系奈恢?,并且支持百分比?/p>
num{
offset-path: path('M250 125c0 69.036-55.964 125-125 125S0 194.036 0 125 55.964 0 125 0s125 55.964 125 125z');
offset-distance: 100%
}
下面是從 0 到 100% 的變化。
默認(rèn)情況下元素的角度也是自適應(yīng)垂直于路徑的,和 textPath 比較類(lèi)似。但是我們可以手動(dòng)指定固定角度,需要 offset-rotate[7],指定為 0deg 就行了。
num{
offset-path: path('M250 125c0 69.036-55.964 125-125 125S0 194.036 0 125 55.964 0 125 0s125 55.964 125 125z');
offset-distance: 100%
}
效果如下,角度已經(jīng)完全歸正了。
如果有類(lèi)似的布局需求,是不是可以參考這個(gè)案例呢?
接下來(lái),我們通過(guò) CSS 變量,把 12 個(gè)數(shù)字自動(dòng)歸位到指定位置。
1
2
3
4
5
6
7
8
9
10
11
12
配合 calc 計(jì)算,完整代碼如下:
num{
position: absolute;
offset-path: path('M250 125c0 69.036-55.964 125-125 125S0 194.036 0 125 55.964 0 125 0s125 55.964 125 125z');
offset-distance: calc( var(--i) * 10% / 1.2 - 25%);
offset-rotate: 0deg;
}
效果如下:
三、自動(dòng)運(yùn)行的指針
三個(gè)指針的繪制應(yīng)該沒(méi)有太大的難度,假設(shè)結(jié)構(gòu)如下:
需要注意一下旋轉(zhuǎn)的中心。
hour{
position: absolute;
width: 4px;
height: 60px;
background: #333;
transform-origin: center bottom;
transform: translateY(-50%) rotate(30deg);
}
min{
position: absolute;
width: 4px;
height: 90px;
background: #333;
transform-origin: center bottom;
transform: translateY(-50%) rotate(60deg);
}
sec{
position: absolute;
width: 2px;
height: 120px;
background: red;
transform-origin: center bottom;
transform: translateY(-50%) rotate(90deg);
}
sec::after{
content: '';
position: absolute;
width: 10px;
height: 10px;
border-radius: 50%;
left: 50%;
bottom: 0;
background: #fff;
border: 4px solid #333;
transform: translate(-50%, 50%);
}
效果如下:
到這一步靜態(tài)布局就算完成了,那么如何運(yùn)行呢?
之前在這篇文章 還在使用定時(shí)器嗎?CSS 也能實(shí)現(xiàn)電子時(shí)鐘 有詳細(xì)講解到,不借助定時(shí)器,也完全可以用 CSS 動(dòng)畫(huà)來(lái)實(shí)現(xiàn)!
回到這里,運(yùn)行的原理很簡(jiǎn)單,就是一個(gè)無(wú)限循環(huán)的 CSS 動(dòng)畫(huà),如下:
@keyframes clock {
to {
transform: translateY(-50%) rotate(360deg);
}
}
不同的是,時(shí)針、分針、秒針的周期不一樣,時(shí)針轉(zhuǎn)一圈是 12 小時(shí)、分針是 60 分鐘、秒針是 60 秒,各自需要換算成 秒數(shù)(CSS 單位只支持 秒 和 毫秒),為了方便測(cè)試,這里將速度調(diào)快了 60s → 6s。
代碼實(shí)現(xiàn)就是(--step 是一分鐘)。
hour{
/**/
transform: translateY(-50%) rotate(0);
animation: clock calc(var(--step) * 60 * 12) infinite;
}
min{
/**/
transform: translateY(-50%) rotate(0);
animation: clock calc(var(--step) * 60) infinite;
}
sec{
/**/
transform: translateY(-50%) rotate(0);
animation: clock var(--step) infinite;
}
效果如下:
是不是有些奇怪?秒針在旋轉(zhuǎn)時(shí)先慢慢變快,然后又慢慢變慢,這是由于默認(rèn)的動(dòng)畫(huà)函數(shù)是ease,所以需要改成linear。
sec{
/**/
animation: clock var(--step) infinite linear;
}
這樣就好多了。不過(guò)平時(shí)所見(jiàn)的時(shí)鐘,秒針通常都那種走一下,停下的,還有一種“滴答滴答”的節(jié)奏感,并不是這種無(wú)縫的。在 CSS 動(dòng)畫(huà)中,是不是有點(diǎn)像階梯狀,沒(méi)錯(cuò),可以用到 CSS 的 steps 函數(shù),不了解這個(gè)的可以參考張老師的這篇文章:CSS3 animation屬性中的steps功能符深入介紹[8],實(shí)現(xiàn)如下:
sec{
/**/
animation: clock var(--step) infinite steps(60);
}
效果如下:
接下來(lái)需要通過(guò) JS 初始化時(shí)間,僅此而已。需要注意的是,在獲取的時(shí)候加上偏移量,比如 12:30,分針其實(shí)是 12.5,以此類(lèi)推。代碼實(shí)現(xiàn)如下:
const d = new Date()
const h = d.getHours();
const m = d.getMinutes();
const s = d.getSeconds();
clock.style.setProperty('--ds', s)
clock.style.setProperty('--dm', m + s/60)
clock.style.setProperty('--dh', h + m/60 + s/3600)
然后 CSS 中可以通過(guò) animation-delay來(lái)指定動(dòng)畫(huà)的起始位置。
hour{
/**/
animation: clock calc(var(--step) * 60 * 12) infinite linear;
animation-delay: calc( -1 * var(--step) * var(--dh) * 60);
}
min{
/**/
animation: clock calc(var(--step) * 60) infinite linear;
animation-delay: calc( -1 * var(--step) * var(--dm));
}
sec{
/**/
animation: clock var(--step) infinite steps(60);
animation-delay: calc( -1 * var(--step) * var(--ds) / 60 );
}
然后加點(diǎn)輪廓裝飾,就實(shí)現(xiàn)了文章開(kāi)頭的效果:
完整代碼可以查看 「文章底部原文鏈接」。
四、簡(jiǎn)單總結(jié)一下
以上就是CSS 繪制時(shí)鐘的全部過(guò)程了,本文更側(cè)重于繪制過(guò)程,特別是是環(huán)形布局技巧,這里簡(jiǎn)單總結(jié)一下:
- 碰到環(huán)形圖案可以想到 conic-gradient。
- 環(huán)形刻度繪制的原理是 conic-gradient 和 MASK 裁剪。
- 文字沿路徑排列可以使用 textPath。
- textPath 不支持改變文字角度。
- offset-path 可以讓元素沿指定路徑進(jìn)行布局。
- offset-path 可以設(shè)置元素在路徑的偏移和角度。
- 時(shí)鐘自動(dòng)運(yùn)行的原理的是 CSS 動(dòng)畫(huà)。
- 時(shí)鐘、分鐘、秒鐘的區(qū)別是動(dòng)畫(huà)時(shí)長(zhǎng)不同。
- “滴答滴答”的效果可以用 steps 實(shí)現(xiàn)。
- 時(shí)間初始化可以通過(guò) animation delay 實(shí)現(xiàn)。
當(dāng)然本文重點(diǎn)并不是在于實(shí)現(xiàn)了一個(gè)時(shí)鐘,而是在于 CSS 繪制技巧的積累和掌握。有了相關(guān)的積累,以后在碰到類(lèi)似的布局,在腦子里過(guò)濾一下,馬上就能找到解決方案。
參考資料
[1]CSS clock: https://codepen.io/xboxyan/pen/JjMWQBP。
[2]conic-gradient: https://developer.mozilla.org/en-US/docs/Web/CSS/gradient/conic-gradient。
[3]repeating-conic-gradient: https://developer.mozilla.org/en-US/docs/Web/CSS/gradient/repeating-conic-gradient。
[4]textPath: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/textPath。
[5]offset-path: https://developer.mozilla.org/en-US/docs/Web/CSS/offset-path。
[6]offset-distance: https://developer.mozilla.org/en-US/docs/Web/CSS/offset-distance。
[7]offset-rotate: https://developer.mozilla.org/en-US/docs/Web/CSS/offset-rotate。
[8]CSS3 animation屬性中的steps功能符深入介紹: https://www.zhangxinxu.com/wordpress/2018/06/css3-animation-steps-step-start-end/。
當(dāng)前文章:一篇帶給你運(yùn)用CSS繪制一個(gè)時(shí)鐘
網(wǎng)頁(yè)路徑:http://m.5511xx.com/article/codcghg.html


咨詢(xún)
建站咨詢(xún)
