SVG Sprites

小圖示的顯示和壓縮技術的演進從 .png.jpg.gif 圖檔、Image Sprites,到 Base64Icon Fonts 再來到 SVG 與 SVG Sprites,來看看到底 SVG Sprites 哪裡好 (✪ω✪)

本文前半段著重在產生 SVG Sprites 的方法和如何使用 SVG Symbols 定位;後半段說明各種 SVG Icon 的使用方式和優缺比較。

如何產生 SVG Sprites?

以下是使用 gulp-svg-sprite 來產生 SVG Sprites。

安裝 svgo 並優化所有 svg 資料夾下的 SVG 圖檔。

npm install svgo --save-dev
svgo svg/*.svg

svgo 對圖檔優化後的結果。

svgo

或使用 gulp-svgo 來做優化,這會在下文中與 gulp-svg-sprite 一併說明。

npm install gulp gulp-plumber gulp-svg-sprite gulp-svgo --save-dev

gulpfile.js 內容如下。打開 symbol 模式,這樣之後就可以使用 SVG Symbols 來定位,超級方便的!

const gulp = require('gulp');
const plumber = require('gulp-plumber');
const svgSprite = require('gulp-svg-sprite');
const svgo = require('gulp-svgo');

const config = {
  mode: {
    symbol: true,
  },
};

gulp.task('default', ['icons']);

gulp.task('watch', () => {
  gulp.watch(['assets/images/svg/*.svg'], ['icons']);
});

gulp.task('icons', () => {
  gulp.src('assets/images/svg/*.svg', { cwd: '' })
    .pipe(plumber())
    .pipe(svgo())
    .pipe(svgSprite(config))
    .on('error', (err) => {
      console.log(err);
    })
    .pipe(gulp.dest('style/generals/sprites/'));
});

點此看完整程式碼。

執行 gulp icons,先將 svg 整個資料夾的 SVG 圖檔優化,再製作成 SVG Sprites 並輸出到 sprites 資料夾底下。

使用 gulp-svg-sprite 產生 SVG Sprites

除了 SVG Symbols,還有多種選擇,例如:利用 class 在 background 使用個別的 SVG 圖檔等,請參考這裡。而為什麼選 SVG Symbols 則可繼續看下文

實作元件包裝 SVG Sprites

使用 React.js 實作一個包裝 SVG Sprites 的元件,這樣引用的時候就只要輸入 icon 名稱和相關資訊就好了,其他亂亂的部份就藏好好不要讓其他人發現。注意,屬性名稱都是 camelCase,因此 <use>xlink:href 要改為 xlinkHref

import React from 'react';
import PropTypes from 'prop-types';
import sprite from './sprite.symbol.svg';

const Icon = ({
  name,
  width,
  height,
  fill,
  title,
}) => {
  return (
    <svg
      className={`icon-${name}`}
      width={width}
      height={height}
      fill={fill}
      role="img"
      aria-label={title}
    >
      { title
        && (
        <title>
          {
            title
          }
        </title>
        )
      }
      <use xlinkHref={`${sprite}#${name}`} />
    </svg>
  );
};

Icon.propTypes = {
  name: PropTypes.string.isRequired,
  width: PropTypes.number,
  height: PropTypes.number,
  fill: PropTypes.string,
  title: PropTypes.string,
};

Icon.defaultProps = {
  width: 40,
  height: 40,
  fill: '#fff',
  title: null,
};

export default Icon;

點此看完整程式碼。

使用這個元件 Icon,預設是白色的。

<Icon name="logo" title="吃什麼,どっち" />

白色的 SVG Icon

使用 fill 更改顏色為深灰色。

<Icon name="logo" title="吃什麼,どっち" fill="#525252" />

灰色的 SVG Icon,使用 fill 更改顏色

打開瀏覽器開發工具來看實作結果。

SVG Icon 實作結果

SVG Icon 使用方式總整理

觀察目前市場上產品使用 SVG Icon 的方式,列舉如下,稍後會詳細說明。

先說結論,目前最佳解是「SVG Sprites by Using SVG Symbols」。

Inline SVG

Inline SVG 是最簡單的方法,直接將 SVG Icon 放到 View 上即可。

範例

<svg
  xmlns="http://www.w3.org/2000/svg"
  width="18"
  height="14"
  viewBox="0 0 18 14">
    <path fill="#342E36" d="..."></path>
</svg>

優點

缺點

SVG in CSS Background

個別 SVG Icon 可利用 CSS 背景來使用 SVG 圖檔。

範例

.icon {
  background-image: url(icon.svg);
  width: 10px;
  height: 10px
}
<span class="icon"></span>

優點

缺點

Using SVG as an <img>

使用 <img> 載入個別 SVG Icon。

範例

<img src="icon.svg" />

優點

缺點

SVG in Data URI

將個別 SVG Icon 轉為 Data URI 再放到 CSS 背景來使用。

範例

.icon {
  background: url(data:image/svg+xml;utf8,<svg width='17' height='15' viewBox='0 0 17 15' xml…46 2.446 0 0 1 7.32 7.24H9.2z' fill='#FFF' fill-rule='nonzero'/></g></svg>) no-repeat 0 0;
}
<span class="icon"></span>

優點

缺點

SVG Sprites by Using SVG Symbols

將個別的 SVG Icon 集合成一個 SVG Sprites,並使用 SVG Symbols 來定位。

範例

<svg class="icon-cart">
  <use xlink:href="sprite.svg#cart"></use>
</svg>
.icon-cart {
  fill: red;
  &:hover {
    fill: white;
  }
}

優點

缺點

SVG Sprites in Data URI

將 SVG Sprites 轉為 Data URI,再利用 CSS 背景定位。

範例

.icon {
  background-image: url('data:image/svg+xml;…');
  background-position: 35% 35%;
}
<span class="icon"></span>

Demo

優點

缺點

總結

目前我考慮兩個方案:SVG in Data URISVG Sprites by Using SVG Symbols,後者勝出的主要原因是彈性和定位方便,而前者雖然有機會讓檔案壓縮得更小,卻要處理瀏覽器相容的問題,因此暫時捨棄 (┐「﹃゚。)

工具

自動將 SVG 資料夾內的所有 Icon 製作成 SVG Sprites 以供使用。

備註

整理目前各大網站 Icon 所使用的技術。

後記

(2018/08/27 更新) 隨著網站的成長, icon 數也跟著增加,這時候做成的 SVG Sprites 就挺大的,雖然有做 preload 但畫面載入時 SVG Sprites 還是會延遲或閃跳,最後只好轉為 base64 只求不發出 HTTP Request,希望之後能找到更好的解法。

References


comments powered by Disqus