SVG 格式在前端領(lǐng)域越來(lái)越流行膨更,它的特性使得各種高清屏幕都不會(huì)失真妙真。于是我收藏一篇個(gè)人覺(jué)得比較好的 SVG 文章。
轉(zhuǎn)自網(wǎng)易考拉前端團(tuán)隊(duì)博客:http://blog.kaolafed.com/2017/04/25/SVG%20Sprite%20%E4%BD%BF%E7%94%A8%E7%AE%80%E4%BB%8B/
SVG簡(jiǎn)介
SVG即可縮放矢量圖形 (Scalable Vector Graphics)的簡(jiǎn)稱, 是一種用來(lái)描述二維矢量圖形的XML標(biāo)記語(yǔ)言. SVG圖形不依賴于分辨率, 因此圖形不會(huì)因?yàn)榉糯蠖@示出明顯的鋸齒邊緣.
icon sprite
當(dāng)我們需要使用多個(gè)icon的時(shí)候, 為了節(jié)省請(qǐng)求和方便管理, 通常會(huì)把icon合并到一個(gè)文件中, 在使用時(shí)再通過(guò)一定的方法從icon集合文件中取出所需的圖形并顯示. 目前使用得最多的應(yīng)該就是我們所熟悉的CSS Sprite和Icon Font.
CSS Sprite
CSS Sprite的原理是將多個(gè)icon按一定規(guī)律整理到一個(gè)圖片文件中, 使用時(shí)利用background-image
和background-position
將圖片中特定部分顯示出來(lái). CSS Sprite技術(shù)已經(jīng)被廣泛應(yīng)用了很長(zhǎng)的一段時(shí)間, 目前有許多自動(dòng)化生成Sprite圖片和CSS文件的工具, 例如(gulp.spritesmith)[https://github.com/twolfson/gulp.spritesmith].
.icon1 {
background-image: url(/res/icon1.png)
}
.icon1-increase {
background-position: -10px -10px;
}
<i class="icon1 icon1-increase"/>
CSS Sprite技術(shù)成熟, 兼容性好, 但是缺點(diǎn)也比較明顯. 如在實(shí)際需求中, 對(duì)應(yīng)形狀相同但顏色不同的icon, 就需要為不同顏色的icon各保存一份; 有時(shí)候需要對(duì)已有icon放大顯示時(shí), 發(fā)現(xiàn)鋸齒嚴(yán)重, 那么又要再保存一份放大版的icon. 因此, Sprite文件會(huì)隨著時(shí)間越變?cè)酱? 同時(shí)內(nèi)容越來(lái)越亂, 逐漸變得難以管理.
Icon Font
Icon Font的基本原理是將Icon定義為圖片字體, 在CSS中用@font-face
引入Icon Font自定義字體, 再利用font-family
和字符碼顯示出指定的圖標(biāo).
@font-face {
font-family: 'iconfont';
src: url(/res/icon2.ttf) format('truetype');
}
.icon2 {
font-family: 'iconfont';
}
<i class="icon2">!</i>
由于使用的是字體, 因此可以通過(guò)color
, font-size
設(shè)置icon的樣式. Icon Font擁有比CSS Sprite圖片更小的文件體積, 維護(hù)也比圖片更方便, 但是icon font通常只能使用單一的顏色, 字體文件生成也比CSS Sprite更復(fù)雜.
SVG Sprite
通常在使用SVG的時(shí)候, 我們是直接寫到svg
標(biāo)簽當(dāng)中:
<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>
此時(shí)SVG圖形會(huì)直接在頁(yè)面當(dāng)中顯示. SVG屬性中, 可以利用(symbol
)[https://developer.mozilla.org/zh-CN/docs/Web/SVG/Element/symbol]封裝圖形, 并利用(use
)[https://developer.mozilla.org/zh-CN/docs/Web/SVG/Element/use]將其實(shí)例化, 從而實(shí)現(xiàn)SVG Sprite的功能.
SVG Sprite實(shí)例:
<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>
這樣就實(shí)現(xiàn)了SVG Sprite.
原理
通過(guò)devtool可以觀察到, use
標(biāo)簽是利用shadow dom實(shí)現(xiàn)的.
它通過(guò)xlink:href
這個(gè)XML的attribute
引用指定的SVG symbol
, 在渲染時(shí)指定symbol
標(biāo)簽中的內(nèi)容就會(huì)被渲染顯示在頁(yè)面當(dāng)中. 這意味著, 如果無(wú)法直接對(duì)use
標(biāo)簽中的shadow dom進(jìn)行訪問(wèn)和修改. 例如像use#rect
這樣的選擇器是無(wú)法生效的, 因此不能通過(guò)一般的css選擇器對(duì)use
中圖形不同的部分進(jìn)行控制.
CSS樣式
更多的時(shí)候, 我們通常都只需要改變圖標(biāo)的大小和顏色, 在SVG Sprite當(dāng)中, 實(shí)現(xiàn)起來(lái)并不復(fù)雜.
- 大小
通過(guò)改變svg
容器的大小, 可以輕松地對(duì)圖標(biāo)大小進(jìn)行控制.
.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>
- 顏色
- 單色
顏色由于不能直接對(duì)use
的shadow root中的圖形標(biāo)簽進(jìn)行選擇, 因此在為圖標(biāo)定義顏色時(shí)需要利用fill: inherit
這個(gè)一屬性.
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
等屬性.
- 多色
除了對(duì)圖標(biāo)整體顏色進(jìn)行定義以外, 還可以根據(jù)需要對(duì)圖形不同部分進(jìn)行顏色定義. 這里使用到了css 的自定義屬性(CSS Custom Properties).
這里需要對(duì)icon.svg
進(jìn)行修改, 將圖形個(gè)部分的顏色設(shè)置為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>
除此以外, 還有其他一些樣式定義的形式, 更詳細(xì)的內(nèi)容可以閱讀Styling SVG <use> Content with CSS.
實(shí)際使用
引用外部svg
上文介紹的是使用內(nèi)聯(lián)svg, 但其實(shí)還可以使用外部svg的.
<svg class="icon">
<use xlink:href="/res/svg/icon.svg#icon-flag"/>
</svg>
然而這種方法不兼容IE9~10, 但如果非要使用外部svg的形式, 可以引入一個(gè)pollyfillsvg4everybody, 當(dāng)運(yùn)行在不支持外部svg的情況下, 這個(gè)pollyfill會(huì)通過(guò)異步請(qǐng)求加載svg委文件并將其注入到頁(yè)面當(dāng)中, 將引用方法轉(zhuǎn)換成內(nèi)聯(lián)svg.
在ftl中使用
可以在svg.ftl
中定義一系列macro
, 用以加載.svg
文件.
<#macro svgicon path>
<#include "${path}">
</#macro>
<#macro svgicon3 >
<@svgicon "./icon3.svg"/>
</#macro>
在頁(yè)面中引用demo.ftl
:
<@svgicon1/>
<svg>
<use xlink:href="#icon-italy"/>
</svg>
在regular中使用
xlink:href
是使用了命名空間的XML特性, 如果是寫在.html
頁(yè)面的標(biāo)簽, 該特性能夠正常被瀏覽器解析并完成svg渲染. 如果該svg變量是通過(guò)DOM API創(chuàng)建出來(lái)的話, 則需要使用特定的方法進(jìn)行處理(SVG with USE tag not rendering).
即需要利用createElementNS
和setAttributeNS
方法在創(chuàng)建的同時(shí)聲明命名空間.
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);
只要是需要?jiǎng)討B(tài)創(chuàng)建use
元素, 都需要使用上面這種方法才能使SVG Sprite中的元素實(shí)例化. 但在目前的Regular(0.4.3)中, 不會(huì)對(duì)命名空間進(jìn)行處理, 這意味著, 如果直接將<svg><use xlink:href/></svg>
寫在Regular組件的模版中時(shí), 該標(biāo)簽是無(wú)法正常渲染的. 為此可以增加一個(gè)指令r-xlink:href
以完成手動(dòng)設(shè)置命名空間的操作.
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了. 當(dāng)然, 首先得確保SVG Sprite被寫到頁(yè)面中.
<svg>
<use r-xlink:href="#icon-italy"/>
</svg>
在Regular的SVG 實(shí)踐中得到了鄭海波大神的指點(diǎn), 否則得繞更大的路才能把問(wèn)題解決, 在此表示感謝.
小結(jié)
對(duì)比前文提到的CSS Sprite和Icon Font, SVG有著明顯的優(yōu)勢(shì):
- 放大縮小不會(huì)失真
- 大小, 顏色等屬性自定義靈活
- 體積小, 同時(shí)管理方便
雖然SVG Sprite有著高度的靈活性, 但于此同時(shí), SVG兼容性有待考究, 同時(shí)其渲染性能也不及圖片和字體那么高, 可能在某些情況下不適用. 不過(guò)在一般的場(chǎng)景中, svg sprite還能夠給開(kāi)發(fā)帶來(lái)很大的便利的.