inline svg
是目前前端圖標解決方案的最優解(當然不僅限於圖標),而且使用方式也及其簡單,只要將svg圖標代碼當成普通的html元素來使用即可,如:
<!-- 繪製右箭頭 -->
<svg viewBox="0 0 1024 1024" height="1em" width="1em" fill="currentColor">
<path d="M665.6 512L419.84 768l-61.44-64 184.32-192L358.4 320l61.44-64 184.32 192 61.44 64z" />
</svg>
<!-- 繪製邊框 -->
<svg viewBox="0 0 20 2" preserveAspectRatio="none" width="100%" height="2px">
<path d="M0 1L20 1" stroke="rgba(0, 0, 0, .5)" stoke-width="2px"></path>
</svg>
將上面的代碼插入html文檔即可以很簡單地繪製出一些圖標。
一般來說,使用
inline svg
作爲圖標使用時,想要保留svg的縱橫比,可以只指定width
屬性,但是一般爲了清晰都同時指定height
屬性。但如果是像上面繪製邊框這種不需要保留縱橫比的情形,可將preserveAspectRatio
設置爲none。
優勢與使用方式
從上面的例子可以看到,將svg直接作爲普通html元素插入文檔中,其本質和渲染出一個div、span等元素無異,天生具有渲染快、不會造成額外的http請求等優勢,除此之外還有以下優勢之處:
樣式控制更加方便
一般來說,我們爲inline svg
頂層的<svg>
元素會設置以下幾個屬性:
- height=“1em” width=“1em” 可以方便地通過設置父元素的font-size屬性控制尺寸
- fill=“currentColor” 可以方便地根據父元素或自身的color屬性控制顏色
但是我們也可以爲其內部的子元素單獨設置樣式,如:
svg path {
fill: rgb(0, 153, 255);
}
在inline svg
中僅有一個<path>
元素時,上面的特性可能用處不大,但是如果某些svg是由多個元素構成時,可以將樣式分別應用的特性就尤爲寶貴了,很容易地就可以解決在字體圖標中不可有多色圖標的問題:
<style>
svg path {
fill: rgb(0, 153, 255);
}
svg path.right {
fill: rgb(30, 185, 133);
}
</style>
<svg viewBox="0 0 1024 1024" width="1em" height="1em" fill="currentColor">
<path d="M256 51.2v256H153.6V512h51.2v51.2h-51.2v307.2H512v51.2H102.4V307.2H0v-256z"></path>
<path d="M256 512h51.2v51.2H256zM358.4 512h51.2v51.2h-51.2z"></path>
<path class="right" d="M460.8 102.4H1024V256H460.8zM460.8 460.8H1024v153.6H460.8V460.8zM460.8 819.2H1024v153.6H460.8V819.2z"></path>
</svg>
動畫控制細化
既然能夠樣式控制能夠細化,那動畫設置自然也能夠具體到各元素,所以這一項嚴格意義上和上一項是一樣的,但是動畫算是樣式中比較獨立的一部分,所以此處單獨拎出來闡述。
這裏要說明一下,使用字體圖標是可以應用動畫的,不過那個動畫是整體動畫:
<!-- 字體圖標整體動畫 -->
<style>
/* 定義icon font */
.icon {
font-family: 'iconfont' !important;
/* ... */
}
.icon-smile:before {
content: '\e938';
}
/* 定義動畫 */
@keyframes loadingIcon {
from {
transform: rotate(0);
}
to {
transform: rotate(360deg);
}
}
.icon {
animation: loadingIcon 2s infinite;
}
</style>
<i class="icon icon-smile"></i>
上面的動畫會將icon-smile圖標整體做旋轉,如果我們只是想對其中的部分應用動畫就不行了。
而在inline svg
中,只需爲想要設置動畫的部分元素設置class
,然後在CSS中定義動畫即可,如要查看針對svg某些部分應用動畫,可查看此例。
如需對svg中各部分分別應用樣式,則在設計svg時最好不要將各部分都編於一組,可以將應用相同樣式的部分進行分別編組,其他不需要設置樣式的部分編爲一組,這樣我們在應用樣式時,只需爲對應的
<g>
標籤設置class
屬性即可。
一般在拿到svg文件後,推薦使用svgo優化svg代碼,節省體積,但是如果我們需要針對性設置樣式時則需要謹慎使用,因爲優化代碼會進行路徑合併等操作,可能我們想要設置的子元素已經不是獨立的了。
不必加載非必要資源
對於字體圖標來說,首頁就需要加載全部的字體文件;對於svg sprites來說,一般需要往<body>
首位插入隱藏的一堆symbol
或defs
代碼合集(可通過對index.html
預插入或者通過js代碼插入)。上述兩種方式都相當於全量加載,對於用戶來說可能根本“看不全”這些圖標或代碼。而通過inline svg
的方式則不會,因爲它是直接渲染在頁面上,不涉及資源加載操作,不會造成浪費。
對單頁面應用來說,配合如webpack
的splitChunks
配置可縮短首屏加載時間;對多頁面應用來說優勢更加明顯,不必爲每個頁面都處理svg相關資源的引入。
inline svg的複用及組件化
上面提到inline svg
的優勢,但是也很容易看出其劣勢:複雜,一個<p>
可以很明顯地表示一段文字,但是一個<svg>
想要描述出一個圖標就有些複雜了,想要表達內容愈豐富,插入文檔中的代碼段就愈複雜,雖然svg本身改動的頻率不高,如有改動也是整體替換,但是它破壞了整個文件的可維護性。而且還存在多處引用同一個inline svg
片段的情況,因爲它基本上是由一些無規律的指令和數組組成的,故針對其進行全局搜索都不現實。所以同一個inline svg
必須能夠進行復用,而組件就是爲了解決複用而生的,而且將inline svg
封裝成組件也相當簡單,下面以Vue和React爲例展示一下inline svg
的組件實現:
React實現
// any-inline-svg-component.tsx
import React from 'react';
interface SVGIconProps {
width?: string;
height?: string;
fill?: string;
style?: React.CSSProperties;
className?: string;
onClick?: (event: React.MouseEvent<SVGSVGElement>) => any;
}
export default (props: SVGIconProps) => {
return (
<svg viewBox="0 0 1024 1024" {...props}>
{/* 內部實現 */}
</svg>
);
}
// 使用inline svg組件
import AnySvgIcon from './any-inline-svg-component'
<AnySvgIcon width="16px" height="16px" />
Vue實現
// any-inline-svg-component.vue
<template>
<svg viewBox="0 0 1024 1024" :width="width" :height="height" :fill="fill">
<!-- 內部實現 -->
</svg>
</template>
<script lang="ts">
import { Vue, Prop, Component } from 'vue-property-decorator'
@Component
export default class AnySvgIcon extends Vue {
@Prop({ default: '1em' }) public width!: string
@Prop({ default: '1em' }) public height!: string
@Prop({ default: 'currentColor' }) public fill!: string
// 其他屬性
}
</script>
// 使用inline svg組件
<template>
<AnySvgIcon />
</template>
<script lang="ts">
import { Vue, Component } from 'vue-property-decorator'
import AnySvgIcon from './any-inline-svg-component.vue'
@Component({
components: {
AnySvgIcon,
},
})
class App extends Vue {
// bala...
}
</script>
請注意安裝
typescript
和vue-property-decorator
等依賴。
如果工程內使用svg較少,可手動將這些svg文件內容處理成相應的組件。但當工程內使用svg很多的話,可參考ant design icons的處理方式,將svg文件批量處理成相應的組件。
更多
上面的例子中,我們都寫死了viewBox
爲0 0 1024 1024,這個屬性是可以改變的,它只是定義了一個座標系統,不代表佔據頁面的空間大小(由width、height指定),而且和設計svg文件時使用的畫布大小有關,大家只要堅持一種設計風格即可,有關svg的設計,可參考字體圖標的使用與設計。
inline svg
是一個很強大的工具,不僅僅能用作圖標,搭配上CSS和其特有的屬性可以很簡單地實現很多效果,大家多多探索。