一步步打造自己的純CSS單標籤圖示庫

深海魚在掘金發表於2017-11-28

圖示作為網頁設計中的一部分,其在凸顯網頁重要元素特性,視覺互動、引導以及網頁裝飾等充當的角色作用舉足輕重。由於圖示普遍具有尺寸小的特點,在專案實踐時不宜將每個圖示作為單個圖片元素進行載入,這會增加Http請求,影響網頁的效能。因此,在實際中,我們可能見到以下一些常見的解決方案:

  • 將多個圖示按照一定排列順序合併在一個圖片裡(即sprite圖),再通過CSS設定元素的background-position來為元素設定背景從而展示圖示
  • 將單個圖示元素轉成base64格式,並在CSS宣告背景
  • 使用SVG來繪製圖示
  • 使用字型圖示
  • 使用CSS來繪製圖示
  • ...

以上方式都可以很好的實現功能,各有各的優缺點。在移動端的某些情況下,我個人比較偏好使用CSS來實現一些簡單的小圖示,原因有以下幾點:

  • 適應性和定製性強,如可以隨意改變顏色,大小
  • 佔用空間小
  • 在移動端相容性高
  • 可以不斷使自己熟悉CSS3的各個屬性並得以應用

而由於CSS3的普及和在各大瀏覽器的不斷增強支援,使CSS具有更大的可能性和能力去繪製更多樣化,更復雜的圖示。當然,也有不少人反對web圖示使用CSS繪製的,在這裡不加以討論。如果你也反對,不妨以當樂趣的心態去看待。

本文將單獨講解如何用CSS繪製一些圖示。而由於用CSS實現圖示繪製,偶爾意味著你需要用更復雜的html結構去支援圖示的繪製,所以本文講解的將是單標籤CSS圖示。這樣可以實現類似僅用img標籤或者單個標籤應用字型庫實現圖示繪製的效果。講解如何繪製之前,先給大家看看前陣子得閒繪製的若干個單標籤CSS圖示。

你需要掌握的CSS屬性

繪製圖示,單從繪製來講,無非就是畫點、線、面。然後將多個點線面組合得到圖示。因此,你至少應該掌握以下CSS屬性的應用

  • 盒子模型
  • border屬性的應用(很重要,可以參考)
  • position的各個屬性值的應用
  • transform變形
  • outline,box-shadow(常見於多邊框繪製)
  • CSS漸變(常用於圖示中透明過渡)
  • 類和偽元素的應用
  • transitionanimation(如果要繪製動態圖示,本文僅講解靜態圖示)

需要掌握的主要為以上內容,有些特殊的處理可能還需要其他一些CSS屬性的應用。

幾個說明

  • 由於大部分情況下圖示的大小按照所處環境上下文的字型大小來決定,所以本文所有例子的大小單位大部分使用em,按照當前字號來設定大小
  • 有些border屬性沒有指明border-color,如border-top: .4em solid,是因為border-color預設繼承了字型顏色
  • 所有圖示僅作為例子展示,實現方法多樣,不代表最佳實踐
  • CSS圖示不適合在實踐中大量使用,你可以以當作樂趣和練習CSS屬性應用的心態去看待本文

基本元素的繪製

border屬性繪製元素

border除了作為簡單的繪製邊框以外,還可以繪製三角形,梯形,星形等任意的多邊形,以下為繪製的兩個三角形和梯形,更多的應用可以參考
《border屬性的多方位應用和實現自適應三角形》這篇文章,裡面全面詳細的介紹了用border繪製各種多邊形。

<div class="triangle1"></div>
<div class="triangle2"></div>
<div class="trapezoid"></div>複製程式碼
.triangle1 {/*銳角三角形*/
    width: 0;
    height: 0;
    border-top:50px solid transparent;
    border-bottom:100px solid #249ff1;
    border-left: 30px solid transparent;
    border-right: 100px solid transparent;
}
.triangle2 {/*直角三角形*/
    width: 0;
    height: 0;
    border-top: 80px solid transparent;
    border-bottom: 80px solid #ff5b01;
    border-left: 50px solid #ff5b01;
    border-right:50px solid transparent;
    }
.trapezoid {/*梯形*/
    width:0;
    height:0;
    border-top:none;
    border-right:80px solid transparent;
    border-bottom:60px solid #13dbed;
    border-left: 80px solid #13dbed;
}複製程式碼

border-radius繪製元素

border-radius主要用於繪製圓點、圓形、橢圓、圓角矩形等形狀,以下為簡單繪製的兩個圖形。


<div class="circle"></div>
<div class="ellipse"><div>複製程式碼
.circle,.ellipse {
    width: 100px;
    height: 100px;
    background: #249ff1;
    border-radius: 50%;
}
.ellipse {
    width: 150px;
    background: #ff9e01;
}複製程式碼

border-radius屬性實際上可以設定最多8個值,通過改變8個值可以得到許多意想不到的影像,如圖(該圖來源於這裡

更多關於border-radius屬性的特點和應用請參考張鑫旭大神寫的《秋月何時了,CSS3 border-radius知多少?》

box-shadow繪製元素

對於box-shadow,其完整的宣告為box-shadow: h-shadow v-shadow blur spread color inset,各個值表示的意義分別為:水平方向的偏移,垂直方向的偏移,模糊的距離(羽化值),陰影的擴充套件大小(不設定或為0時陰影大小與主體的大小一致),陰影的顏色和是否使用內陰影。實際應用時可以接收3-6個值,對應分別如下:

  • 3個值: h-shadow v-shadow color
  • 4個值: h-shadow v-shadow blur color
  • 5個值: h-shadow v-shadow blur spread color
  • 6個值: h-shadow v-shadow blur spread color inset

同時,border-shadow接受由多個以上各種值組成的以逗號分隔的值,通過這一特性,我們可以實現如多重邊框的等效果。以下我們用該屬性來實現一個單標籤且不借助偽元素的新增圖示代表目標的的圖示。(為方便觀察,這裡將新增符號的實現部分用紅色代替)

<div class="plus"></div>
<div class="target"></div>複製程式碼
.plus {
    width: 30px;
    height: 30px;
    margin-left: 50px;/*由於box-shadow不佔空間,常常需要新增margin來校正位置*/
    background: #000;
    box-shadow: 0 -30px 0 red, 
                0 30px 0 red,
                -30px 0 0 red, 
                30px 0 0 red;
}
.target {
    width: 30px;
    height: 30px;
    background: red;
    border-radius: 50%;
    margin-left: 50px;
    box-shadow: 0 0 0 10px #fff,
                0 0 0 20px red, 
                0 0 0 30px #fff,
                0 0 0 40px red;
}複製程式碼

結果如下:

以上,新增符號採用多個由四個值組成的以逗號分隔的值來設定加號的四個角達到效果,目標圖示則通過多次設定陰影大小大於主體大小的值疊加成了多個圓環來實現。

由於box-shadow不佔據空間,實際應用中常常需要設定margin來矯正圖示的位置,這和outline屬性一致,兩個屬性最大的不同是outline形成的區域不會因為border-radius而形成圓角。

使用CSS漸變來繪製圖示

CSS3的漸變屬性十分強大,理論上通過漸變可以繪製出任何的圖形,漸變的特性和使用足足可以寫一篇長文,以下為一個例子

<div class="gradient"></div>複製程式碼
.gradient {
    position: relative;
    width: 300px;
    height: 300px;
    border-radius: 50%;
    background-color: silver;
    background-image: linear-gradient(335deg, #b00 23px, transparent 23px),
                      linear-gradient(155deg, #d00 23px, transparent 23px),
                      linear-gradient(335deg, #b00 23px, transparent 23px),
                      linear-gradient(155deg, #d00 23px, transparent 23px);
    background-size: 58px 58px;
    background-position: 0px 2px, 4px 35px, 29px 31px, 34px 6px;
}複製程式碼

以上將得到如下結果

關於線性漸變屬性的用法,可以參考這裡

更多牛逼的例子可以觀摩《CSS SECRETS》作者繪製的漸變背景點選這裡

小試牛刀

有了以上的基礎之後,我們就可以一步步打造一個屬於自己的單標籤CSS圖示庫。文章最前面提到了下面這些圖示

現在抽取其中幾個嘗試繪製實現一下。

  • 杯子

首先我們對杯子進行拆分,很容易想到將杯子拆分為杯身和杯柄兩個部分。拆分之後,應該用兩個圓角矩形來實現,至於杯子的輪廓(圖中黑色部分),我們可以選擇用邊框border來實現,border的顏色按實際填充即可(本文未指定時預設為跟隨當前字型的顏色)。由於是單標籤實現,因此,我們還要藉助一個偽元素(杯柄)來實現這個圖示。於是就有了以下的樣式:

.cup {
    display: inline-block;
    width: .9em;
    height: .4em;
    border: .25em solid;
    border-bottom: 1.1em solid;
    border-radius: 0 0 .25em .25em;
}
cup:before {
    position: absolute;
    right: -.6em;
    top: 0;
    width: .3em;
    height: .8em;
    border: .25em solid;
    border-left: none;
    border-radius: 0 .25em .25em 0;
    content: '';
}複製程式碼

以上便實現了一個杯子的圖示,通過調整邊框顏色(預設為與當前字型顏色相同)和寬度即可實現不同大小的圖示。

  • 心形

仔細觀察這個心形,它實際上應該可以近似看做是由以下兩個形狀按照一定的角度旋轉和平移組成。

因此我們可以用兩個元素來繪製這兩個部分,通過設定背景色和border-radius,然後按照一定的角度旋轉並平移則可得到。為了方便定位,這裡我們用兩個偽元素來繪製這兩個部分。為了方便觀察,我們先設定如下樣式

.heart{
    display: inline-block;
    margin-top: 1.5em;
    width:50px;
    height: 50px;
    background: green;
}
.heart:before,.heart:after {
  position: absolute;
  width: 1em;
  height: 1.6em;
  background: #000;
  border-radius: 50% 50% 0 0;
  content: '';
  bottom: 0;
}
.heart:before {
  -webkit-transform: rotate(45deg);
  -webkit-transform-origin: 100% 100%;
  right: 0;
  background: red;
  opacity: .5;
  z-index: 5;
}
.:after {
  -webkit-transform: rotate(-45deg);
  -webkit-transform-origin: 0 100%;
  left: 0;
  opacity: .8;
}複製程式碼

可以看到,此時兩個元素將因為.heart元素的寬度而撐開了一段距離,實際上應該是A點和B點重合於一點。因此,如果我們把.heart的寬高都設為0,則得到如下結果:

到這裡為止,已經完成了一個心形的繪製,但是仔細看,左右兩側會有一個邊角突出,這是因為旋轉的角度不夠導致。可以通過調大角度或者設定一個較大的圓角(適應性更高)來修復此問題。這裡將旋轉的角度調整為48deg。修改顏色和透明度後即得到如下結果:

  • 相機

至於這個相機,由於前面已經介紹瞭如何用border-radius繪製一個代表目標的圖示,問題就變得很簡單了。整個相機分為三個部分,通過定位即可實現。以下直接貼實現程式碼。

.camera {
    display: inline-block;
    border-style: solid;
    border-width: .65em .9em;
    border-radius: .1em;
}
.camera:before {
    position: absolute;
    top: -.3em;
    left: -.3em;
    width: .4em;
    height: .4em;
    border-radius: 50%;
    border: .1em solid #FFF;
    box-shadow: 0 0 0 .08em,0 0 0 .16em #fff;
    content: '';
}
.camera:after {
    position: absolute;
    top: -.5em;
    left: .5em;
    width: .2em;
    border-top: .125em solid #FFF;
    content: '';
}複製程式碼
  • 月亮

月亮這個圖示乍一看似乎挺難實現,但如果掌握了border-radius屬性的應用,其實是相當的容易,以下為完整的CSS樣式:

.moon {
    display:inline-block;
    height: 1.5em;
    width: 1.5em;
    box-shadow: inset -.4em 0 0;
    border-radius: 2em;
    transform: rotate(20deg);
}複製程式碼

其核心是將陰影的模式設定為inset通過調整引數,可以得到不同的月亮形狀,如下圖:

總結

用CSS繪製圖示其實其核心就是將拆分後的多個元素經過旋轉和平移得到。歸根結底還是要掌握CSS相關屬性的應用。一般由以下幾個步驟:

  • 分析圖示,拆分為小元素
  • 繪製小元素
  • 定位(平移和旋轉等)
  • 設定在父級元素的定位(如用margin定位)

如果不限制於單個標籤實現,那麼可以繪製更多複雜的圖示。以下為個人在繪製圖示時的一點小體會:

  • 圖示的顏色可以使用border屬性去繪製
  • border-color大部分情況下可以不設定,預設跟隨圖示當前所在文字的字型顏色
  • 圖示的大小單位可以使用em,即相對於當前所在文字的字型大小進行計算,這應該符合與大部分場合,適應性也高,複用性強。(以上例子中均僅需要調整父級的字型大小即可以放大或縮小圖示)

相關閱讀

CSS魔法之重拾CSS樂趣(上)

CSS魔法之重拾CSS樂趣(下)

CSS3 Patterns Gallery

相關文章