新聞中心
某日,群里有這樣一個(gè)問題,如何實(shí)現(xiàn)這樣的表盤刻度:

創(chuàng)新互聯(lián)基于成都重慶香港及美國(guó)等地區(qū)分布式IDC機(jī)房數(shù)據(jù)中心構(gòu)建的電信大帶寬,聯(lián)通大帶寬,移動(dòng)大帶寬,多線BGP大帶寬租用,是為眾多客戶提供專業(yè)鄭州服務(wù)器托管報(bào)價(jià),主機(jī)托管價(jià)格性價(jià)比高,為金融證券行業(yè)服務(wù)器托管,ai人工智能服務(wù)器托管提供bgp線路100M獨(dú)享,G口帶寬及機(jī)柜租用的專業(yè)成都idc公司。
這其實(shí)是個(gè)挺有意思的問題,方法也有很多。
單標(biāo)簽,使用 conic-gradient 實(shí)現(xiàn)表盤刻度
最簡(jiǎn)單便捷的方式,就是利用角向漸變的方式 conic-gradient,代碼也非常簡(jiǎn)單,首先,我們實(shí)現(xiàn)一個(gè)重復(fù)角向漸變:
div {
width: 300px;
height: 300px;
border-radius: 50%;
background: repeating-conic-gradient(
#000 0, #000 .8deg, transparent 1deg, transparent calc((360 / 60) * 1deg)
);
}其實(shí)比較難理解的是 calc((360 / 60) * 1deg),這是因?yàn)楸肀P一共通常有 60 個(gè)刻度。效果大概是這樣:
接下來,只需要將中間鏤空即可。如果背景色是白色,直接疊加一個(gè)圓形即可,當(dāng)然,更好的方式是通過 mask 屬性進(jìn)行鏤空:
{
background: repeating-conic-gradient(
#000 0, #000 .8deg, transparent 1deg, transparent calc(360 / 60 * 1deg)
);
mask: radial-gradient(transparent 0, transparent 140px, #000 140px)
}這樣,我們就得到了一個(gè)表盤刻度:
這是使用一個(gè)標(biāo)簽就能實(shí)現(xiàn)的方式,當(dāng)然,缺點(diǎn)也很明顯:
- 鋸齒感嚴(yán)重,漸變的通病。
- 由于是使用的角向漸變,刻度存在頭重腳輕的現(xiàn)象,越向內(nèi)部,寬度越窄(刻度愈大,差異愈加明顯)。
使用多個(gè)標(biāo)簽實(shí)現(xiàn)
如果不介意使用太多的標(biāo)簽,那么通常而言,更容易想到的方法就是利用 60 個(gè)標(biāo)簽,加上旋轉(zhuǎn)實(shí)現(xiàn):
// ... 一共 60 個(gè)
.g-item {
position: absolute;
width: 4px;
height: 12px;
background: #000;
left: 0;
top: 0;
transform-origin: 0 150px;
}
@for $i from 1 through 60 {
.g-item:nth-child(#{$i}) {
transform: rotate(#{($i - 1) * 6deg});
}
}
像是這樣,我們通過 60 個(gè) div 標(biāo)簽,利用 SASS 的 for 語法減少重復(fù)的代碼量,批量實(shí)現(xiàn)每個(gè)元素逐漸繞一點(diǎn)旋轉(zhuǎn)一定的角度,也是可以實(shí)現(xiàn)一個(gè)表盤刻度的:
這個(gè)方案的好處是,每個(gè)刻度粗細(xì)一致,并且,不會(huì)產(chǎn)生鋸齒。
借助 -webkit-box-reflect 減少標(biāo)簽數(shù)
當(dāng)然,上述方案的缺點(diǎn)就在于,使用了 60 個(gè)標(biāo)簽去完成這樣一個(gè)簡(jiǎn)單的圖形,有點(diǎn)太奢侈了。
我們希望盡可能的優(yōu)化優(yōu)化標(biāo)簽的個(gè)數(shù)。此時(shí),我們很容易的想到 -- -webkit-box-reflect,倒影效果屬性。
-webkit-box-reflect[1] 是一個(gè)非常有意思的屬性,它讓 CSS 有能力像鏡子一樣,反射我們?cè)卦纠L制的內(nèi)容。
-webkit-box-reflect 的語法非常簡(jiǎn)單,最基本的用法像是這樣:
div {
-webkit-box-reflect: below;
}其中,below 可以是 below | above | left | right 代表下上左右,也就是有 4 個(gè)方向可以選。
假設(shè)我們有如下一張圖片:
div {
background-image: url('https://images.pokemontcg.io/xy2/12_hires.png');
}加上 -webkit-box-reflect: right,也就是右側(cè)的倒影:
div {
background-image: url('https://images.pokemontcg.io/xy2/12_hires.png');
-webkit-box-reflect: right;
}效果如下,生成了一個(gè)元素右側(cè)的鏡像元素:
借助 -webkit-box-reflect: right,我們至少可以從 60 個(gè)標(biāo)簽減少到 15 個(gè)標(biāo)簽的使用。簡(jiǎn)單的嵌套兩層即可。
我們簡(jiǎn)單改變一下 HTML 結(jié)構(gòu):
// ... 一共 16 個(gè)
這一次,我們只需要實(shí)現(xiàn) 1/4 圓的刻度即可:
@for $i from 1 through 16 {
.g-item:nth-child(#{$i}) {
transform: rotate(#{($i - 1) * 6deg});
}
}我們可以得到這樣一個(gè)圖形:
基于這個(gè)圖形,我們只需要先向左側(cè)倒影一次,再向下倒影一次即可:
.g-container {
-webkit-box-reflect: below;
}
.g-parent {
-webkit-box-reflect: left;
}效果如下:
大致的效果就出來了,當(dāng)然,0點(diǎn)、3點(diǎn)、6點(diǎn)、9點(diǎn)上左下右 4 個(gè)刻度有點(diǎn)問題。不過 -webkit-box-reflect 也提供距離調(diào)整功能,再簡(jiǎn)單修改下代碼:
.g-container {
-webkit-box-reflect: below 4px;
}
.g-parent {
-webkit-box-reflect: left -4px;
}這次,效果就是我們想要的最終效果:
我們就成功地借助 -webkit-box-reflect 節(jié)約了 3/4 的標(biāo)簽數(shù)量。完整的代碼:CodePen Demo -- Clock ticks[2]。
-webkit-box-reflect 與剪紙藝術(shù)
到這里,我不由得想到,這種對(duì)折、再對(duì)折,鏡像再鏡像的方式,與我們小時(shí)候的折紙藝術(shù)非常的類似。
那么,基于這樣一個(gè)模板:
.g-container {
-webkit-box-reflect: below;
}
.g-parent {
-webkit-box-reflect: left;
}我只需要繪制 .g-item 里面的內(nèi)容,將他通過 2 次 -webkit-box-reflect 鏡像,就能得到一個(gè)剪紙圖形。
而如何得到隨機(jī)有意思的不規(guī)則圖形呢?
clip-path 是個(gè)很不錯(cuò)的選擇,我們通過 clip-path 隨機(jī)對(duì)一個(gè)矩形進(jìn)行裁剪:
.g-item {
width: 150px;
height: 150px;
background: #000;
clip-path: polygon(25% 0%,71% 66%,59% 0%,79% 23%,95% 4%,100% 40%,77% 100%,38% 100%,47% 71%,36% 30%,23% 60%,0% 100%,5% 37%);
}效果如下:
經(jīng)過兩次鏡像后的效果如下:
是不是有那么點(diǎn)意思了?可以隨機(jī)利用 clip-path 多嘗試幾次,可以得到不同的效果:
CodePen Demo -- Pure CSS Page Cutting[3]。
-webkit-box-reflect 配合 clip-path 配合 mask
但是上面的圖形看著還是太簡(jiǎn)單了,幾個(gè)原因,一是對(duì)折的次數(shù)和角度不夠,缺少對(duì)折次數(shù)和不同角度的對(duì)折,二是圖形不夠負(fù)責(zé)。
我又想起了之前看到過的一篇類似的剪紙相關(guān)的文章 -- Paper Snowflakes: Combining Clipping and Masking in CSS[4]。
再上述的基礎(chǔ)上,還使用了 mask,將圖形切割的更細(xì)。
我們?cè)賮硪淮?,還是同樣的結(jié)構(gòu),當(dāng)然,為了得到更負(fù)責(zé)的圖形,我們?cè)O(shè)置了 4 個(gè) .g-item:
首先,還是設(shè)置一個(gè) clip-path 切割后的圖形:
.g-item:nth-child(1) {
width: 150px;
height: 150px;
background: #000;
clip-path: polygon(17% 41%,6% 39%,16% 91%,18% 78%,56% 11%,28% 71%,99% 67%,25% 65%,69% 72%,46% 28%,90% 76%,67% 34%,48% 30%,79% 36%,59% 15%,23% 92%,16% 1%,32% 81%,72% 38%,50% 59%,71% 98%,66% 87%,83% 14%,36% 71%,49% 7%,9% 25%,52% 76%,10% 83%,17% 41%);
}效果如下:
這個(gè)圖可能它會(huì)特別的奇怪,沒有問題,我們繼續(xù)。
如果,我們將一個(gè)矩形,從左下角開始算,分為 4 份,那么每一個(gè)份就是 90° / 4 = 22.5°,我們希望切割得到其中一份:
我們可以借助 mask 去完成這個(gè)切割:
.g-item:nth-child(1) {
width: 150px;
height: 150px;
background: #000;
clip-path: polygon(.....);
mask: conic-gradient(from 0turn at 0 100%, #000, #000 22.5deg, transparent 22.5deg, transparent);
}上述的圖形,就被切割成了這樣:
OK,基于此,我們可以得到同樣的第二份圖形,但是我們給它加一個(gè) rotateY(180deg):
.g-item:nth-child(2) {
width: 150px;
height: 150px;
background: #000;
clip-path: polygon(.....);
mask: conic-gradient(from 0turn at 0 100%, #000, #000 22.5deg, transparent 22.5deg, transparent);
transform: rotateY(180deg);
}效果如下:
我們?cè)偻ㄟ^ rotateZ(),給第二圖形旋轉(zhuǎn)一定的角度,讓他和第一個(gè)貼合在一起:
.g-item:nth-child(2) {
clip-path: polygon(.....);
mask: conic-gradient(from 0turn at 0 100%, #000, #000 22.5deg, transparent 22.5deg, transparent);
transform: rotateY(180deg) rotateZ(-45deg);
}就得到了一個(gè)斜向角度的鏡像圖像:
因?yàn)?.g-item 被切割成了 4 份,所以第 3、4 個(gè)圖形如法炮制即可,這樣,整個(gè) .g-item 的效果如下:
再打開 -webkit-box-reflect,整個(gè)的圖形效果如下:
這樣,一個(gè)剪紙圖形就誕生啦!
當(dāng)然,為了得到不一樣的效果,我們可以借助 JavaScript 去隨機(jī)生成 CSS 中的各類參數(shù),完整的代碼,大概是這樣:
.g-item {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background: #000;
transform-origin: 0 100%;
clip-path: var(--polygon, polygon(40% 0%,0% 91%,52% 100%,0% 37%,77% 23%,77% 76%,43% 22%,55% 88%,100% 100%,100% 10%));
}
.g-item {
mask: conic-gradient(from 0turn at 0 100%, #000, #000 22.5deg, transparent 22.5deg, transparent);
}
@for $i from 1 through 5 {
.g-item:nth-child(#{$i}) {
transform: rotateZ(calc(22.5deg * #{$i - 1}));
}
}
.g-item:nth-child(2) {
transform: rotateY(180deg) rotateZ(-60deg);
}
.g-item:nth-child(4) {
transform: rotateY(180deg) rotateZ(-105deg);
}
.g-container {
-webkit-box-reflect: below;
}
.g-parent {
-webkit-box-reflect: left;
}
const ele = document.querySelectorAll('.g-item');
document.addEventListener('click', function(e) {
let num = Math.floor(Math.random() * 30 + 10);
const maskR = Math.floor(Math.random() * 22.5 + 22.5 ) + 'deg';
const r1 = Math.floor(Math.random() * 100) + '%';
const r2 = Math.floor(Math.random() * 100) + '%';
let polygon = 'polygon(' + r1 + ' ' + r2 + ',';
for (let i=0; i const newR1 = Math.floor(Math.random() * 100) + '%';
const newR2 = Math.floor(Math.random() * 100) + '%';
polygon += newR1 + ' ' + newR2 + ','
}
polygon += r1 + ' ' + r2 + ')';
[...ele].forEach(item => {
item.setAttribute('style', `--polygon:${polygon};-webkit-mask:conic-gradient(from 0turn at 0 100%, #000, #000 ${maskR}, transparent ${maskR}, transparent)`);
});
}); 整個(gè)效果是純 CSS 的,JavaScript 在這里的作用僅僅是輔助生成隨機(jī)的 clip-path 圖案,以及不同的 mask 切割角度,這樣,每次點(diǎn)擊鼠標(biāo),我們都能得到不同的隨機(jī)剪紙圖案:
看看這個(gè)簡(jiǎn)單錄制的 GIF:
完整的代碼,你可以猛擊這里 CodePen Demo -- Pure CSS Art Page Cutting[5]。
最后
本文到此結(jié)束,希望對(duì)你有幫助 ????
參考資料
[1]-webkit-box-reflect: https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-box-reflect。
[2]CodePen Demo -- Clock ticks: https://codepen.io/Chokcoco/pen/eYybEpW。
[3]CodePen Demo -- Pure CSS Page Cutting: https://codepen.io/Chokcoco/pen/oNpJewQ。
[4]Paper Snowflakes: Combining Clipping and Masking in CSS: https://css-irl.info/paper-snowflakes-combining-clipping-and-masking-in-css/。
[5]CodePen Demo -- Pure CSS Art Page Cutting: https://codepen.io/Chokcoco/pen/zYpeJBZ。
名稱欄目:離譜的CSS!從表盤刻度到剪紙藝術(shù)
地址分享:http://www.fisionsoft.com.cn/article/dpipdsj.html


咨詢
建站咨詢
