SVG簡介
SVG即可縮放向量圖形 (Scalable Vector Graphics)的簡稱, 是一種用來描述二維向量圖形的XML標記語言. SVG圖形不依賴於解析度, 因此圖形不會因為放大而顯示出明顯的鋸齒邊緣.
icon sprite
當我們需要使用多個icon的時候, 為了節省請求和方便管理, 通常會把icon合併到一個檔案中, 在使用時再通過一定的方法從icon集合檔案中取出所需的圖形並顯示. 目前使用得最多的應該就是我們所熟悉的CSS Sprite和Icon Font.
CSS Sprite
CSS Sprite的原理是將多個icon按一定規律整理到一個圖片檔案中, 使用時利用background-image
和background-position
將圖片中特定部分顯示出來. CSS Sprite技術已經被廣泛應用了很長的一段時間, 目前有許多自動化生成Sprite圖片和CSS檔案的工具, 例如(gulp.spritesmith)[github.com/twolfson/gu…].
.icon1 {
background-image: url(/res/icon1.png)
}
.icon1-increase {
background-position: -10px -10px;
}複製程式碼
<i class="icon1 icon1-increase"/>複製程式碼
CSS Sprite技術成熟, 相容性好, 但是缺點也比較明顯. 如在實際需求中, 對應形狀相同但顏色不同的icon, 就需要為不同顏色的icon各儲存一份; 有時候需要對已有icon放大顯示時, 發現鋸齒嚴重, 那麼又要再儲存一份放大版的icon. 因此, Sprite檔案會隨著時間越變越大, 同時內容越來越亂, 逐漸變得難以管理.
Icon Font
Icon Font的基本原理是將Icon定義為圖片字型, 在CSS中用@font-face
引入Icon Font自定義字型, 再利用font-family
和字元碼顯示出指定的圖示.
@font-face {
font-family: `iconfont`;
src: url(/res/icon2.ttf) format(`truetype`);
}
.icon2 {
font-family: `iconfont`;
}複製程式碼
<i class="icon2">!</i>複製程式碼
由於使用的是字型, 因此可以通過color
, font-size
設定icon的樣式. Icon Font擁有比CSS Sprite圖片更小的檔案體積, 維護也比圖片更方便, 但是icon font通常只能使用單一的顏色, 字型檔案生成也比CSS Sprite更復雜.
SVG Sprite
通常在使用SVG的時候, 我們是直接寫到svg
標籤當中:
<svg xmlns="http://www.w3.org/2000/svg" width="150" height="100" viewBox="0 0 3 2">
<rect width="1" height="2" x="0" fill="#008d46" />
<rect width="1" height="2" x="1" fill="#ffffff" />
<rect width="1" height="2" x="2" fill="#d2232c" />
</svg>複製程式碼
此時SVG圖形會直接在頁面當中顯示. SVG屬性中, 可以利用(symbol
)[developer.mozilla.org/zh-CN/docs/…, 並利用(use
)[developer.mozilla.org/zh-CN/docs/…, 從而實現SVG Sprite的功能.
SVG Sprite例項:
<svg style="height:0;width:0;display:none;" version="1.1" xmlns="http://www.w3.org/2000/svg">
<symbol id="icon-italy" width="150" height="100" viewBox="0 0 3 2">
<rect width="1" height="2" x="0" fill="#008d46" />
<rect width="1" height="2" x="1" fill="#ffffff" />
<rect width="1" height="2" x="2" fill="#d2232c" />
</symbol>
<symbol id="icon-france" width="150" height="100" viewBox="0 0 3 2">
<rect width="1" height="2" x="0" fill="#002496" />
<rect width="1" height="2" x="1" fill="#ffffff" />
<rect width="1" height="2" x="2" fill="#ee2839" />
</symbol>
</svg>
<svg><use xlink:href="#icon-italy"/></svg>
<svg><use xlink:href="#icon-france"/></svg>複製程式碼
這樣就實現了SVG Sprite.
原理
通過devtool可以觀察到, use
標籤是利用shadow dom實現的.
它通過xlink:href
這個XML的attribute
引用指定的SVG symbol
, 在渲染時指定symbol
標籤中的內容就會被渲染顯示在頁面當中. 這意味著, 如果無法直接對use
標籤中的shadow dom進行訪問和修改. 例如像use#rect
這樣的選擇器是無法生效的, 因此不能通過一般的css選擇器對use
中圖形不同的部分進行控制.
CSS樣式
更多的時候, 我們通常都只需要改變圖示的大小和顏色, 在SVG Sprite當中, 實現起來並不複雜.
- 大小
通過改變svg
容器的大小, 可以輕鬆地對圖示大小進行控制.
.icon{
width: 120px;
height: 80px;
}
.icon.icon-small{
width: 60px;
height: 40px;
}複製程式碼
<svg class="icon"><use xlink:href="#icon-italy"/></svg>
<svg class="icon icon-small"><use xlink:href="#icon-italy"/></svg>複製程式碼
- 顏色
- 單色
顏色由於不能直接對use
的shadow root中的圖形標籤進行選擇, 因此在為圖示定義顏色時需要利用fill: inherit
這個一屬性.
svg path{
fill: inherit;
}
.icon2-green{
fill: #008d46;
}
.icon2-red{
fill: #dc352f;
}複製程式碼
<svg class="icon2 icon2-green">
<use xlink:href="#icon-increase"/>
</svg>
<svg class="icon2 icon2-red">
<use xlink:href="#icon-increase"/>
</svg>複製程式碼
利用inherit
, 還可以定義stroke-width
, stroke
等屬性.
- 多色
除了對圖示整體顏色進行定義以外, 還可以根據需要對圖形不同部分進行顏色定義. 這裡使用到了css 的自定義屬性(CSS Custom Properties).
這裡需要對icon.svg
進行修改, 將圖形個部分的顏色設定為fill: var(--*[, default])
的形式.
<symbol id="icon-flag" width="150" height="100" viewBox="0 0 3 2">
<rect width="1" height="2" x="0" style="fill: var(--color0, #008d46)" />
<rect width="1" height="2" x="1" style="fill: var(--color1, #fff)"/>
<rect width="1" height="2" x="2" style="fill: var(--color2, #d2232c)"/>
</symbol>複製程式碼
定義css樣式
.flag-belgium {
--color0: #201b18;
--color1: #f1ee3d;
--color2: #dc352f;
}複製程式碼
<svg class="icon">
<use xlink:href="#icon-flag"/>
</svg>
<svg class="icon flag-belgium">
<use xlink:href="#icon-flag"/>
</svg>複製程式碼
除此以外, 還有其他一些樣式定義的形式, 更詳細的內容可以閱讀Styling SVG Content with CSS.
實際使用
引用外部svg
上文介紹的是使用內聯svg, 但其實還可以使用外部svg的.
<svg class="icon">
<use xlink:href="/res/svg/icon.svg#icon-flag"/>
</svg>複製程式碼
然而這種方法不相容IE9~10, 但如果非要使用外部svg的形式, 可以引入一個pollyfillsvg4everybody, 當執行在不支援外部svg的情況下, 這個pollyfill會通過非同步請求載入svg委檔案並將其注入到頁面當中, 將引用方法轉換成內聯svg.
在ftl中使用
可以在svg.ftl
中定義一系列macro
, 用以載入.svg
檔案.
<#macro svgicon path>
<#include "${path}">
</#macro>
<#macro svgicon3 >
<@svgicon "./icon3.svg"/>
</#macro>複製程式碼
在頁面中引用demo.ftl
:
<@svgicon1/>
<svg>
<use xlink:href="#icon-italy"/>
</svg>複製程式碼
在regular中使用
xlink:href
是使用了名稱空間的XML特性, 如果是寫在.html
頁面的標籤, 該特效能夠正常被瀏覽器解析並完成svg渲染. 如果該svg變數是通過DOM API建立出來的話, 則需要使用特定的方法進行處理(SVG with USE tag not rendering).
即需要利用createElementNS
和setAttributeNS
方法在建立的同時宣告名稱空間.
var use = document.createElementNS(`http://www.w3.org/2000/svg`, `use`);
use.setAttributeNS(`http://www.w3.org/1999/xlink`, `xlink:href`, `#icon-increase`);
document.querySelector(`#svgid`).appendChild(use);複製程式碼
只要是需要動態建立use
元素, 都需要使用上面這種方法才能使SVG Sprite中的元素例項化. 但在目前的Regular(0.4.3)中, 不會對名稱空間進行處理, 這意味著, 如果直接將<svg><use xlink:href/></svg>
寫在Regular元件的模版中時, 該標籤是無法正常渲染的. 為此可以增加一個指令r-xlink:href
以完成手動設定名稱空間的操作.
Regular.directive(`r-xlink:href`, function (elem, val) {
if (val&& val.type === `expression`) {
this.$watch(val, function (newVal) {
elem.setAttributeNS(`http://www.w3.org/1999/xlink`, `href`, newVal);
});
} else {
elem.setAttributeNS(`http://www.w3.org/1999/xlink`, `href`, val);
}
});複製程式碼
那麼在元件模版中就可以像在普通.html
中那樣使用SVG Sprite了. 當然, 首先得確保SVG Sprite被寫到頁面中.
<svg>
<use r-xlink:href="#icon-italy"/>
</svg>複製程式碼
在Regular的SVG 實踐中得到了鄭海波大神的指點, 否則得繞更大的路才能把問題解決, 在此表示感謝.
小結
對比前文提到的CSS Sprite和Icon Font, SVG有著明顯的優勢:
- 放大縮小不會失真
- 大小, 顏色等屬性自定義靈活
- 體積小, 同時管理方便
雖然SVG Sprite有著高度的靈活性, 但於此同時, SVG相容性有待考究, 同時其渲染效能也不及圖片和字型那麼高, 可能在某些情況下不適用. 不過在一般的場景中, svg sprite還能夠給開發帶來很大的便利的.