某日,群里有這樣一個問題,如何實作這樣的表盤刻度:
這其實是個挺有意思的問題,方法也有很多,
單標簽,使用 conic-gradient 實作表盤刻度
最簡單便捷的方式,就是利用角向漸變的方式 conic-gradient,代碼也非常簡單,首先,我們實作一個重復角向漸變:
<div></div>
div {
width: 300px;
height: 300px;
border-radius: 50%;
background: repeating-conic-gradient(
#000 0, #000 .8deg, transparent 1deg, transparent calc((360 / 60) * 1deg)
);
}
其實比較難理解的是 calc((360 / 60) * 1deg),這是因為表盤一共通常有 60 個刻度,效果大概是這樣:
接下來,只需要將中間鏤空即可,如果背景色是白色,直接疊加一個圓形即可,當然,更好的方式是通過 mask 屬性進行鏤空:
{
background: repeating-conic-gradient(
#000 0, #000 .8deg, transparent 1deg, transparent calc(360 / 60 * 1deg)
);
mask: radial-gradient(transparent 0, transparent 140px, #000 140px)
}
這樣,我們就得到了一個表盤刻度:
這是使用一個標簽就能實作的方式,當然,缺點也很明顯:
- 鋸齒感嚴重,漸變的通病
- 由于是使用的角向漸變,刻度存在頭重腳輕的現象,越向內部,寬度越窄(刻度愈大,差異愈加明顯)
使用多個標簽實作
如果不介意使用太多的標簽,那么通常而言,更容易想到的方法就是利用 60 個標簽,加上旋轉實作:
<div >
<div ></div>
// ... 一共 60 個
<div ></div>
</div>
.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 個 div 標簽,利用 SASS 的 for 語法減少重復的代碼量,批量實作每個元素逐漸繞一點旋轉一定的角度,也是可以實作一個表盤刻度的:
這個方案的好處是,每個刻度粗細一致,并且,不會產生鋸齒,
借助 -webkit-box-reflect 減少標簽數
當然,上述方案的缺點就在于,使用了 60 個標簽去完成這樣一個簡單的圖形,有點太奢侈了,
我們希望盡可能的優化優化標簽的個數,此時,我們很容易的想到 -- -webkit-box-reflect,倒影效果屬性,
-webkit-box-reflect 是一個非常有意思的屬性,它讓 CSS 有能力像鏡子一樣,反射我們元素原本繪制的內容,
-webkit-box-reflect 的語法非常簡單,最基本的用法像是這樣:
div {
-webkit-box-reflect: below;
}
其中,below 可以是 below | above | left | right 代表下上左右,也就是有 4 個方向可以選,
假設我們有如下一張圖片:
<div></div>
div {
background-image: url('https://images.pokemontcg.io/xy2/12_hires.png');
}
加上 -webkit-box-reflect: right,也就是右側的倒影:
div {
background-image: url('https://images.pokemontcg.io/xy2/12_hires.png');
-webkit-box-reflect: right;
}
效果如下,生成了一個元素右側的鏡像元素:
借助 -webkit-box-reflect: right,我們至少可以從 60 個標簽減少到 15 個標簽的使用,簡單的嵌套兩層即可,
我們簡單改變一下 HTML 結構:
<div >
<div >
<div ></div>
// ... 一共 16 個
<div ></div>
</div>
</div>
這一次,我們只需要實作 1/4 圓的刻度即可:
@for $i from 1 through 16 {
.g-item:nth-child(#{$i}) {
transform: rotate(#{($i - 1) * 6deg});
}
}
我們可以得到這樣一個圖形:
基于這個圖形,我們只需要先向左側倒影一次,再向下倒影一次即可:
.g-container {
-webkit-box-reflect: below;
}
.g-parent {
-webkit-box-reflect: left;
}
效果如下:
大致的效果就出來了,當然,0點、3點、6點、9點上左下右 4 個刻度有點問題,不過 -webkit-box-reflect 也提供距離調整功能,再簡單修改下代碼:
.g-container {
-webkit-box-reflect: below 4px;
}
.g-parent {
-webkit-box-reflect: left -4px;
}
這次,效果就是我們想要的最終效果:
我們就成功地借助 -webkit-box-reflect 節約了 3/4 的標簽數量,完整的代碼:CodePen Demo -- Clock ticks
-webkit-box-reflect 與剪紙藝術
到這里,我不由得想到,這種對折、再對折,鏡像再鏡像的方式,與我們小時候的折紙藝術非常的類似,
那么,基于這樣一個模板:
<div >
<div >
<div ></div>
</div>
</div>
.g-container {
-webkit-box-reflect: below;
}
.g-parent {
-webkit-box-reflect: left;
}
我只需要繪制 .g-item 里面的內容,將他通過 2 次 -webkit-box-reflect 鏡像,就能得到一個剪紙圖形,
而如何得到隨機有意思的不規則圖形呢?
clip-path 是個很不錯的選擇,我們通過 clip-path 隨機對一個矩形進行裁剪:
.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%);
}
效果如下:

經過兩次鏡像后的效果如下:
是不是有那么點意思了?可以隨機利用 clip-path 多嘗試幾次,可以得到不同的效果:
CodePen Demo -- Pure CSS Page Cutting
-webkit-box-reflect 配合 clip-path 配合 mask
但是上面的圖形看著還是太簡單了,幾個原因,一是對折的次數和角度不夠,缺少對折次數和不同角度的對折,二是圖形不夠負責,
我又想起了之前看到過的一篇類似的剪紙相關的文章 -- Paper Snowflakes: Combining Clipping and Masking in CSS,
再上述的基礎上,還使用了 mask,將圖形切割的更細,
我們再來一次,還是同樣的結構,當然,為了得到更負責的圖形,我們設定了 4 個 .g-item:
<div >
<div >
<div ></div>
<div ></div>
<div ></div>
<div ></div>
</div>
</div>
首先,還是設定一個 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%);
}
效果如下:
這個圖可能它會特別的奇怪,沒有問題,我們繼續,
如果,我們將一個矩形,從左下角開始算,分為 4 份,那么每一個份就是 90° / 4 = 22.5°,我們希望切割得到其中一份:
我們可以借助 mask 去完成這個切割:
.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,基于此,我們可以得到同樣的第二份圖形,但是我們給它加一個 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);
}
效果如下:
我們再通過 rotateZ(),給第二圖形旋轉一定的角度,讓他和第一個貼合在一起:
.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-item 被切割成了 4 份,所以第 3、4 個圖形如法炮制即可,這樣,整個 .g-item 的效果如下:
再打開 -webkit-box-reflect,整個的圖形效果如下:
這樣,一個剪紙圖形就誕生啦!
當然,為了得到不一樣的效果,我們可以借助 JavaScript 去隨機生成 CSS 中的各類引數,完整的代碼,大概是這樣:
<div >
<div >
<div ></div>
<div ></div>
<div ></div>
<div ></div>
</div>
</div>
.g-container,
.g-parent {
position: relative;
display: flex;
width: 150px;
height: 150px;
}
.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<num; 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)`);
});
});
這樣,每次點擊滑鼠,我們都能得到不同的隨機剪紙圖案:
看看這個簡單錄制的 GIF:
完整的代碼,你可以猛擊這里 CodePen Demo -- Pure CSS Art Page Cutting
最后
本文到此結束,希望對你有幫助 ??
更多精彩 CSS 技術文章匯總在我的 Github -- iCSS ,持續更新,歡迎點個 star 訂閱收藏,
如果還有什么疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/469728.html
標籤:Html/Css
上一篇:函式
下一篇:vue - Vue組件化編程
