inline svg的使用

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>首位插入隱藏的一堆symboldefs代碼合集(可通過對index.html預插入或者通過js代碼插入)。上述兩種方式都相當於全量加載,對於用戶來說可能根本“看不全”這些圖標或代碼。而通過inline svg的方式則不會,因爲它是直接渲染在頁面上,不涉及資源加載操作,不會造成浪費。

對單頁面應用來說,配合如webpacksplitChunks配置可縮短首屏加載時間;對多頁面應用來說優勢更加明顯,不必爲每個頁面都處理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>

請注意安裝typescriptvue-property-decorator等依賴。

如果工程內使用svg較少,可手動將這些svg文件內容處理成相應的組件。但當工程內使用svg很多的話,可參考ant design icons的處理方式,將svg文件批量處理成相應的組件。

更多

上面的例子中,我們都寫死了viewBox0 0 1024 1024,這個屬性是可以改變的,它只是定義了一個座標系統,不代表佔據頁面的空間大小(由width、height指定),而且和設計svg文件時使用的畫布大小有關,大家只要堅持一種設計風格即可,有關svg的設計,可參考字體圖標的使用與設計

inline svg是一個很強大的工具,不僅僅能用作圖標,搭配上CSS和其特有的屬性可以很簡單地實現很多效果,大家多多探索。

參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章