gulpでSVGスプライト！gulp-svgstoreやgulp-cheerioなどを使ったタスク

# SVGスプライトを使いたいと思った経緯

SVGを使用するにあたり、自分の要求としては、

- 色はcssで自由に変えたい
- オリジナルのデザインアイコンをSVGにしたい
- 軽く、かつ効率的に簡単にSVG使いたい
- スマートに使いたい
- アニメーションは特に考えてない

でした。

今まではimg要素でそのままSVG読み込んだりしていたのですが、
その方法ではcssで色を自由に変えることができません。
[desvg.js](http://benhowdle.im/deSVG/)というjavascriptを入れれば変更できるようになるのですが、
わざわざそのためにjs読み込むのもな…と思ったので
手軽にできそうな他の方法を探すことにしました。

------
【余談】
SVGを使う前提ですが、私は
「オリジナルのアイコンイラストを自前で作って色をhoverやページごとに変えたい」
などの場合にSVG使用を考えています。
font awesomeやicomoonなどで使える素材は、アイコンフォントの方が使い勝手がいいのでそちらを使用しています。なのでSVGは一部のイラストのみで、ほとんどの通常アイコンはフォントです。（ロゴは色んな理由で画像）

---


というわけで、色々調べていたところ
SVGスプライトは使い勝手が良さそうだな〜、と
ぼんやり思ってgulpで良い方法がないか調べてみることに。

# SVGスプライトをgulpで

以前gulpでスプライト画像の生成もやっていたので、
SVGでも同じ感覚でできるだろう、、！と思い
参考記事をみながら進めていたのですが。。。
何度やってもうまくいかずやたら時間がかかりました…(´；ω；｀)

記述が古いのか？エラーになってしまったり、私の頭じゃ理解できず使いこなせなかったりで悶々しつつ、なんとかやりたいことだけができる状態にもっていけました。なのでメモついでにこちらに書かせていただいております！

結果的には、[gulp-svgstore](https://github.com/w0rm/gulp-svgstore)の使い方をみながら必要なタスクを整理して書いた感じです。（英語つらい）

たぶんそこをみたら普通にわかると思うんですが、
英語の苦手意識半端ないのでだいぶ時間かかりました、、_(┐「ε:)__
もし私のように英語が苦手な方に、参考なれば嬉しいです！

#導入について

まずは導入の手順を紹介します。
以下の手順で使えるようになりました！

##インストール

今回、私がインストールしたものは以下です。
※gulpはすでに動く前提で、gulp自体の設定は割愛させていただきます。

- [gulp-svgstore](https://github.com/w0rm/gulp-svgstore)
→複数のSVGを1つのSVGファイルにまとめる

- [gulp-cheerio](https://github.com/knpwrs/gulp-cheerio)
→htmlやxmlを書き換える
※SVGはxmlで書かれているので、このプラグインを使って余分な記述を削除します。

- [gulp-svgmin](https://www.npmjs.com/package/gulp-svgmin)
→SVGをミニファイ化

- [gulp-path](https://www.npmjs.com/package/gulp-path)
→ファイルのパスを作成

フォールバック用のpng画像をSVGから自動で生成するプラグインもあったのですが、
IE11以降の対応でよかったのでこちらでは使用していません。
[gulp-svg2png](https://github.com/akoenig/gulp-svg2png)を使用することで可能になります。

とりあえず一式インストールします。
packege.jsonに入れる場合は--save-devも記述します。

```lang:
npm install gulp-svgstore --save-dev
```
```lang:
npm install gulp-cheerio --save-dev
```
```lang:
npm install gulp-svgmin --save-dev
```
```lang:
npm install gulp-path --save-dev
```

##gulpfile.jsの記述

gulpfile.jsにSVGタスクを記述していきます。
まずは先程インストールしたプラグインを読み込み。

```lang:
var svgstore = require('gulp-svgstore');
var cheerio = require('gulp-cheerio');
var svgmin = require('gulp-svgmin');
var path = require('path');
```

そして、必要なタスクを記述します。

```lang:
// svg
gulp.task('svgstore', function () {
  return gulp
  .src('test/src/*.svg')
  .pipe(svgmin(function (file) {
      var prefix = path.basename(file.relative, path.extname(file.relative));
      return {
          plugins: [{
              cleanupIDs: {
                  prefix: prefix + '-',
                  minify: true
              }
          }]
      }
  }))
  .pipe(svgstore({ inlineSvg: true }))

  .pipe(cheerio({
      run: function ($) {
          $('[fill]').removeAttr('fill');
          $('[stroke]').removeAttr('stroke');
          $('svg').attr('style','display:none');
      },
      parserOptions: { xmlMode: true }
  }))
  .pipe(gulp.dest('test/dest'));
});

```

私の場合は自動実行をかけたので、このあとに下記も記述しました。

```lang:
// 自動実行
gulp.task("default",['server'], function() {
    gulp.watch("test/src/*.svg",["svgstore"]);
});
```

これで、ルート直下の`/test/src/`に
IllustratorやSketchなどで保存した単体のSVGをつっこめば、
即座にひとまとめになった新しいsrc.svgがその都度`test/dest`に生成されます！

#htmlの書き方

htmlでは、use属性を使ってひとまとめになったsrc.svgを参照し、
`#`以下を元の単体SVGファイルの名称にすればOKです。かんたーん！！
これならオリジナルのSVGアイコンも怖がらずにもりもり作って導入していけそうです。

```lang:
<svg class="icon">
  <use xlink:href="test/dest/src.svg#logo" />
</svg>
```

そしてcssで、該当のSVGに対してサイズや色を指定します。
レスポンシブにも使いやすいし色の変化つけれるので嬉しいです。

```lang:
.icon {
  width: 200px;
  height: 200px;
  fill: #aaa;
}
```

#非対応ブラウザについて

MS Edge・IE11以下では、
useを使ったuseを使った外部SVGに対応していないようです。

##IE11の対応

IE11でも表示対応させるために[svg4everybody](svg4everybody)を読み込みます。
※[svgxuse](https://github.com/Keyamoon/svgxuse)もオススメいただきました！

今回はとりあえずsvg4everybodyを入れ、
IE10、IE11での表示が確認できました。

```lang:
  <script src="assets/js/svg4everybody.js"></script>
  <script>svg4everybody();</script>
  <meta http-equiv="X-UA-Compatible" content="IE=Edge">
```

##MS Edge
Edgeでは、useを使った外部SVGに対応していないようです。
ただ、[こちらの記事](http://parashuto.com/rriver/development/easiest-svg-sprite-icon-system#browser-support)ではEdgeHTML13で機能が実装されたようです。
私の方ではまだEdgeでの確認はできておりません＞＜

#ざっくり説明

私も完璧に理解しきっていないので
説明の仕方がおかしいところもあるかもしれませんが、、
このタスクについてざっと説明します。

```lang:
  .src('test/src/*.svg')
  .pipe(svgmin(function (file) {
      var prefix = path.basename(file.relative, path.extname(file.relative));
      return {
          plugins: [{
              cleanupIDs: {
                  prefix: prefix + '-',
                  minify: true
              }
          }]
      }
  }))
  .pipe(svgstore({ inlineSvg: true }))
```

このタスクを通して、バラバラだった単体のSVGが`<symbol>`要素で
ひとまとめにしたsvgファイルとして書き出されます。
書き出されたSVGの名前は、元の単体SVGファイルが入った
ディレクトリ名になるのでここでは「src.svg」が`test/dest`に自動生成されます。
そしてsvgminを通してSVGをミニファイ化し、
さらにpathを使って`<symbol>`要素のid名に元の単体SVGファイルの名称が入るようにしています。

```lang:
  .pipe(cheerio({
      run: function ($) {
          $('[fill]').removeAttr('fill');
          $('[stroke]').removeAttr('stroke');
          $('svg').attr('style','display:none');
      },
      parserOptions: { xmlMode: true }
  }))
```

cheerioを通してxmlの記述を書き換えています。
例えば、IllustratorなどでアイコンをSVGで書き出す際、
ベタ色がCMYK100%以外だとfill属性がついてしまいます。
これがある状態ではcssでfillの色変更ができません。
その為removeAttrでfill属性を削除するようにしています。

同じく、アウトラインがあると、stroke属性がついてしまいます。
ただ、そもそもアウトラインがある状態でSVG書き出しはしないと思うのですが、、
念のためということであった場合は削除するようになっています。
また、インラインでSVGを読み込んだときにSVGスプライトが表示されないよう、
`<svg>`は`display:none`で不可視としています。

ちなみに`parserOptions: { xmlMode: true }`ですが、
これがないと属性が全部小文字になってしまうようです。
viewBox属性が「viewbox」となってしまうのでこちらは必須です。

そして最後に`.pipe(gulp.dest('test/dest'));`で、
`test/dest`に結果が書き出されるようになっています。


###参考サイト様

- [Gulpを使ったSVGスプライトのアイコンシステムとワークフローの作り方](http://parashuto.com/rriver/development/easiest-svg-sprite-icon-system)
- [SVGシンボル化（SVGスプライト）](http://nakagaw.hateblo.jp/entry/2016/09/01/194043)
- [svg-spriteで無駄の無いSVGスプライトを生成する](http://design.dena.com/engineering/svg-sprite/)

#おわり

以上でした！
無事生成されてよかった、、

たくさん細かいアイコンとかイラストを使っていると
途中でデザインや色を変える場合も多いのでこの方法は助かりそうですね。

とりあえずimgでSVGファイル、がちょっと不便だったので、
良い方法ができるようになってよかったです。