通常一個網頁上面,或多或少都會用到一些小圖標,展現這些小圖標的方法有不少種。最簡單的作法就是將UI圖上面的每一個小圖標都保存爲圖片,一個小圖標就一張圖片。但這也是比較笨的方法,由於瀏覽器同一時間最多加載的資源是有限的,例如IE7是2個,IE8是6個,chrome是6個,火狐是8個。若是網頁上面有不少張零碎的小圖片,致使請求的次數太多,等待加載狀態中的資源會不少,明顯影響性能。所以,一個改進的辦法是使用sprites圖,將多張小圖放在一張大圖,而後限定展現區域的大小,同時改變圖片的顯示位置background-position來顯示不一樣的圖標,遊戲裏面常用這種技術,大大減小瀏覽器請求的次數。淘寶網就使用這種技術:css
可是要看到這種方法也是有缺點的,即內存和CPU的使用增長,對於移動端低內存和CPU的設備來講,可能會有壓力。使用sprites圖,網上有不少在線的功具能夠生成,同時會生成各個小圖標的position位置,例如http://csssprites.com/html
第二種改進的辦法是使用base64的編碼方式。將原始二進制的圖片編碼爲base64,而後使用css的background: url(data:image/png;base64,%encoding%)的方式,例如百度的首頁搜索欄右邊的話筒就是用這樣的方式:android
將圖片進行編碼,可使用在線工具base64 image,進行轉換。轉換以後,你會發現生成的編碼特別長,其字節數甚至比原始的照片大,大約大33%。以上面的話筒爲例,原始照片爲1.3kb,而base64的編碼須要1.7kb。同時,另一個問題是對base64的解析速度比原始二進制的要慢。更嚴重的一個問題是,若是使用太多的base64,會使得css文件太大,下載和解析的時間較長,致使頁面短期的空白loading狀態,效果可能還不如分開使用一張張圖片。它的優勢是不須要借用額外的圖片文件,詳細的分析能夠看這篇文章。ios
第三種方法是使用CSS的技巧,這種方法通常只適用於比較簡單的圖案,例如三角形、五角星、愛心等。例如,若是想要畫一個向上的三角形可使用下面的方法:css3
.tri{ width: 0; height: 0; border-left: 50px solid transparent; border-right: 50px solid transparent; border-bottom: 100px solid red; }
它的原理是將一個div的width和height設置成0,那就剩下四個border,四個角都是三角形,令其它三個角不顯示,只留下底部那個角,就是一個向上的三角形。要注意設置左右角的寬度,目的是設置三角形上面兩條邊的長度,再將它們隱藏。更多CSS圖形參考css shape。這種方法看似完美,由於不管是空間佔用仍是解析速度都比前面兩個方法好,可是這種方式是不天然的,你沒法輕易地改變圖形的大小去適應你的頁面,若是你不知道它畫的原理是怎麼樣的。第二是沒法容易地畫出一些較爲複雜的圖案,例如爲了畫三個小黃人,花費了2000多行的CSS代碼。另一個缺點是,它是一個空的span或者div,對於屏幕閱讀者來講是不可見的。git
第四種方法是使用icon font,將ui圖裏的icon導出製做成一個字體庫,而後跟正常的字體同樣使用,具體制做的方法可參考這篇文章。通常來講,icon font是從svg等矢量格式來的,經過PS導出png的方法可能會存在一些問題。boostrap的glyphicon就是使用icon font。使用時,先用@font-face導入字體(font-face的使用見這篇文章),而後利用一個span,設置font-family爲剛剛導入的字體,再經過僞類before或after,屬性content的值爲對應圖標的編碼。或者是,直接在html文件裏直接插入該圖標的編碼。以下所示:github
使用這種方法的優勢是很大程序上減小了圖片須要的空間,能夠自由改變大小,改變顏色,支持IE6及以上。缺點是隻適用於純色的圖標。手機淘寶和百度就使用了這種技術web
icon-font的製做方法可參見博主的另一篇文章:把UI圖裏小圖標製成icon fontchrome
還有一種辦法是使用Unicode字符,Unicode也提供了不少的圖標和表情,例如打勾,✔ ✓ ☑,使用起來最爲簡單,惋惜的是,不一樣的字體差異很大,有些字體沒有這些符號,甚至是同一個字體在不一樣的設備上看起來也會有差別,例如✔在安卓機上的形狀這是樣的(中間的勾),而在ios上是這樣的
,一樣都是使用了微軟雅黑字體。bootstrap
上面說起的各類方法都存在一個缺點,沒有語義性,都是一個空的span和div,對屏幕閱讀者不可見。本文介紹一種新的畫小圖標的方法,使用svg結合css3的新屬性clip-path。這種方法的優勢是具有語義性,不管在性能仍是佔用的空間都具備優點。clip就是裁剪的意思,clip-path本來的用處是用來裁剪圖片,如:
上面,指定裁剪的路徑爲一個橢圓,x軸上的半徑爲裁剪區域的50%,y軸的半徑爲裁剪區域的40%,圓心在(50%, 50%)的位置。在這個橢圓形的封閉區域外的全部元素都不會被瀏覽器渲染出來,使用時要帶有-webkit-前綴和標準的兩種形式。Clippy這個網站能夠在線裁剪,當前最新版本的chrome和safari都支持基本形狀的裁剪。除了橢圓外還支持rect(長方形)、cirle(圓形)、inset(帶圓角的長方形)、polygon(多邊形),具體使用可結合上面的博客和網站進行探索。最後一種形式,是使用html裏定義的svg元素做爲裁剪的目標,這也正是clip-path的生命力所在。由於svg自己提供了豐富的語義定義,能夠製做豐富多彩的矢量圖形,更重要的是svg可進行可視化編輯,如AI,inkscape,還有一些在線的編輯器,如svg-editor。關於svg的基本介紹,可參考mdn的教程。
除了裁圖片,利用clip-path的裁剪功能,能夠用來製做圖標。原理就是用一個div,設置background顏色和width/height值,而後製做一個圖標的svg路徑,用來裁剪div,就會顯示出相應的小圖標了。以打勾的圖標爲例:
首先,製做一個打勾的svg:
<svg width="0" height="0"> <defs> <clipPath id="tick-mask" clipPathUnits="objectBoundingBox"> <path fill="red" stroke="red" stroke-width="1" stroke-miterlimit="10" d="m0.1165671,0.4703638l0.0852069,-0.0852042l0.2337128,0.2335306l0.389592,-0.3894064l0.0852045,0.0852087l-0.4747964,0.4747913z" id="svg_8" clip-rule='evenodd'/> </clipPath> </defs> </svg>
注意這裏,不是使用基本形狀,而是使用了svg裏的path,貝塞爾曲線,也就是PS/AI裏面的鋼筆工具,在d裏面定義路徑是如何移動和彎曲的。繪出的形狀要放在clipPath標籤裏,給這個clipPath添加一個id,在下面的CSS裏將會使用到,同時設置clipPathUnits爲objectBoundingBox,做用是將單位設置成比例[0,1],這樣就能夠適配出不一樣大小的形狀。clipPathUnits有兩個取值,另一個取值是userSpaceOnUse,是默認值,通常單位爲px。
形狀畫好了以後,因爲要求背景是紅色的,勾是白色的,所以先用一個div,設置紅色背景和圓角,再用一個白底的span裁出一個勾的形狀。以下:
<div class="icon"> <span class='tick'></span> </div>
.icon{ width: 100px; height: 100px; background: #ff7443; text-align: center; background: #ff7443; border-radius: 100px; } .tick{ display: inline-block; -webkit-clip-path: url(#tick-mask); clip-path: url(#tick-mask); /* 在這裏對白底的span進行剪切 */ width: 90%; height: 90%; background: white; margin-top: 5%; }
這樣就能夠了。這篇文章做者做了一個圓形菜單,還有結合css3的動畫,做了一些頗有趣的動態效果。
關於兼容性,IE和edge全部版本不支持clip-path,android的瀏覽器支持url參數的clip-path,可是UC和微信的內置瀏覽器不支持,微博的瀏覽器是支持的,firefox支持帶url參數的。chrome支持-webkit-前綴的,包括基本的形狀和url,safari/ios支持標準形式的,可是safari/ios在渲染上有bug,只要css文件裏出現了clip-path,任何元素只要帶position爲relative/absolute的都會隱藏掉了,解決辦法是,在這些元素里加多一個css屬性:-webkit-transform: translateZ(0)加大渲染權重,這樣就能顯示出來了。還有可能會出現其它沒法渲染的狀況,例如,同一個id的clip-path只能渲染出第一個,接下來的都消失了,也能夠用這種辦法解決,可是若是渲染太重,在chrome等其它瀏覽器會出現顯示的問題,會顯示錯亂。所以這個問題比較麻煩,h5開發的時候須要注意。
對於沒法支持的瀏覽器,改用其它的辦法,得作個區分。能夠借鑑modernizr提供的辦法,頁面加載時,首先建立一個svg和一個div,設置這個div的clip-path CSS屬性,而後調用getComputedStyle看是否仍有剛剛設置的屬性,若是有說明支持,沒有說明不支持。若是支持就給body添加一個has-clip-path的類,不支持就爲no-clip-path,而後在須要使用圖標的元素的css前面加多一個clip-path的類,有和沒有兩個。這樣就達到了區分的目的,不支持的就使用其它的方式。
<body> <svg style="display:none" width="0" height="0"><defs><clipPath id="_svg"><path d="M 0 0 L 0 0"></path></clipPath></defs></svg> <div style="-webkit-clip-path:url(#_svg);clip-path:(#_svg);display:none" id="_test"></div> <script> var style = document.defaultView.getComputedStyle(document.getElementById("_test"), null); var body = document.getElementsByTagName("body")[0]; if(style.WebkitClipPath !== "url(#_svg)" && style.clipPath !== "url(#_svg") body.className = "no-clip-path"; else body.className = "has-clip-path"; </script>
<!--body的其它元素--> </body>
原本可使用svg和clip-path作爲h5開發,可是考慮到安卓上的某些國內瀏覽器不支持,以及safari讓人頭疼的渲染問題,因此就目前的狀況來講應用到生產環境仍不太樂觀。因此在PC的web端使用sprites圖,在移動的h5端使用icon font並靈活結合其它方法。
注意到,icon-font和clip-path本質都是同樣的,都是使用了svg,只是使用的方式不一樣。所以在提供icon font圖標的網站上,如icomoon和fontello上,可將圖標的svg製做字體,也可做爲clip-path使用。
參考:
1. CSS vs. SVG: Shapes and Arbitrarily-Shaped UI Components 這篇文章比較了使用CSS和svg畫圖標的兩種方法,強調了使用svg畫圖的優勢。
2. SVG Tutorial,MDN一個關於svg的簡明易懂的入門教程。
3. icomoon和fontello,提供icon-font/svg小圖標的網站。
4. Clippy在線操做clip-path