HTMLでもCSSでも設定出来るgulp-svg-sprite


アイコンがCSS３では表現できないデザイン、かつ色違いで複数箇所使われている時に楽をしたくてgulpのsvg spriteを導入した時のメモです。

## **今までのもやもや**


1. 色違いっていうだけで書き出す画像が増える。
   (それぞれ画像名を考えるのがめんどくさい。特にarrow_xxとか似てるものが多いとなおさら。)

2. hover時のためにわざわざbackground-imageなどで画像を入れ替えないといけない。
   (画像名を書き換えるのがめんどくさい。)

3. PC/SPそれぞれの解像度で書き出さないといけない。
   (書き出すのがめんどくさい。)

4.既存のプロジェクトですでにbackground-imageで画像を指定している箇所はそのままcss上で差し替えたい。

[spritesmith](https://github.com/twolfson/gulp.spritesmith)は使ってるけど、上記の4しかカバー出来ないので[gulp-svg-sprite](https://github.com/jkphl/gulp-svg-sprite)を採用。
しかし、インラインでspriteが使えるのは大変便利だけど、
> 4.既存のプロジェクトですでにbackground-imageで画像を指定している箇所はそのままcss上で差し替えたい。

これが結構重要だった・・・。
すでにほとんどのアイコンはpng画像をbackground-imageで指定している・・・。
全アイコンsvg化を進めるには、**既存のアイコン指定方法を変えず、修正箇所は最小限としたい。**
ただし、今後色違いアイコンが増える可能性があるため、**cssでcolor指定出来るようにもしたい。**

今までのCSSスプライトより求めるものが多くなってきた。
そこで

## **今回の条件**

条件1:background-imageで指定が出来ること。
条件2:HTMLにタグをインラインで埋め込めること。
条件3:PCはIE11/Edge対応、スマホはAndroid4.1〜対応出来ること。
条件4:現在のディレクトリ構成を壊さず、出力されるファイルはそれぞれ適したディレクトリに置かれること。

こちらを実現させるためのサンプルディレクトリ構成はこちら。

```
.
├── dest // 出力先 静的なファイル
│   ├── css
│   ├── img
│   └── js
├── src 
│    ├──sass
│    │    ├── _style.scss // ページのスタイルscss
│    │    └── main.scss // 各scssをimportするscss
│    └── img //sprite化したいsvg
│ 　　　　　　　     ├── beer.svg
│    　     └── cat.svg
├── gulpfile.js
├── package.json
├── node_modules
└── index.html

```
[http://icooon-mono.com/](http://icooon-mono.com/ "icoon-mono")からこの2つの画像をsprite化

![cc095ad43ebbb1bf17e6adcfc24d64da.png](https://qiita-image-store.s3.amazonaws.com/0/125435/2008fec7-b6b2-5c7e-ffc4-b9602bf7d565.png)






## **gulpfile.js**
[https://github.com/jkphl/svg-sprite](https://github.com/jkphl/svg-sprite)こちらをこつこつ　google翻訳しながら・・・。
最低限のパッケージしか入れてません。また、svg-spriteのtaskのみ抜粋しています。[(すべてのコード)](https://github.com/Satsuki-Isomata/test_svg-sprite "github")

```javascript:gulpfile.js
const gulp = require('gulp'),
      sass = require('gulp-sass'),
      svgSprite = require('gulp-svg-sprite'),
      autoprefixer = require('gulp-autoprefixer'),
      browser = require('browser-sync');


// 他taskは省略

/*******************************
svg-sprite
****************************** */

 // path
const imgDir = './src/img/*.svg';
const spriteDir = '../dest/img/';

// mode config
const view = {// view mode
  // cssでsvg指定
  mode: {
    view: {
      render: {
        scss: {
          dest: '../src/sass/sprite/_svgSprite.scss',// sass出力先
        },
      },
      sprite: `${spriteDir}sprite.view.svg`,// svg出力先
      dimensions: false, // sass上でのサイズ用classを生成しない
      bust: false // キャッシュ用パラメータを削除
    }
  },
  shape : {
    transform: [
      {
        svgo: {
          plugins: [
            { 'removeTitle': true }, // titleを削除
            { 'removeXMLNS': true }, // xmlnを削除
            { 'removeDimensions': true } // width/heightを削除
          ]
        }
      }
    ]
  },
  svg : {
    xmlDeclaration: false // xml宣言を削除
  }
};
const symbol = {// symbo mode
  mode: {
    symbol: {
      sprite: `${spriteDir}sprite.symbol.svg`,
    }
  },
  shape : {
    transform: [
      {
        svgo: {
          plugins: [
            { 'removeTitle': true },
            { 'removeStyleElement': true }, // styleを削除
            { 'removeAttrs': { 'attrs': 'fill' } }, // fillを削除
            { 'removeXMLNS': true },
            { 'removeDimensions': true }
          ]
        }
      }
    ]
  },
  svg : {
    xmlDeclaration: false
  }
};

gulp.task('svg-sprite', () => {
  gulp.src(imgDir)
    .pipe(svgSprite(symbol))
    .pipe(gulp.dest('.'));// sprite.symbol.svgを生成
  gulp.src(imgDir)
    .pipe(svgSprite(view))
    .pipe(gulp.dest('.'));// sprite.view.svgと_svgSprite.scssを生成
});

gulp.task('default', ['browserSync','watch-sass','sass','svg-sprite']);
```
完成したらgulpを回す。

```terminal:terminal
$gulp
[16:48:45] Using gulpfile ~/test_svg-sprite/gulpfile.js
[16:48:45] Starting 'browserSync'...
[16:48:46] Finished 'browserSync' after 43 ms
[16:48:46] Starting 'watch'...
[16:48:46] Finished 'watch' after 1.36 ms
[16:48:46] Starting 'sass'...
[16:48:46] Finished 'sass' after 5.86 ms
[16:48:46] Starting 'svg-sprite'...
[16:48:46] Finished 'svg-sprite' after 4.08 ms
[16:48:46] Starting 'default'...
[16:48:46] Finished 'default' after 17 μs
[Browsersync] Access URLs:
 ----------------------------------
       Local: http://localhost:3000
    External: http://10.0.4.31:3000
 ----------------------------------
          UI: http://localhost:3001
 UI External: http://10.0.4.31:3001
 ----------------------------------
[Browsersync] Serving files from: .
[16:48:46] Starting 'sass'...
[16:48:46] Finished 'sass' after 1.53 ms
[Browsersync] Reloading Browsers...
```
成功！！
こんな感じにファイルが追加された。

```
.
├── dest 
│   ├── css
│   │    └── main.css　// コンパイルされたcss
│   ├── img
│   │    ├── sprite.symbol.svg // symbolモードで生成されるsprite
│   │    └── sprite.view.svg // viewモードで生成されるsprite
│   └── js
├── src 
│    ├──sass
│    │    ├── sprite
│    │    │      └── _svgPrite.scss // viewモードで生成されるscss
│    │    ├── _style.scss
│    │    └── main.scss
│    └── img
│ 　　　　　　　     ├── beer.svg
│    　     └── cat.svg
├── gulpfile.js
├── package.json
├── node_modules
└── index.html

```
## やったこと
・ CSSで指定できるview(sprite.view.svg)とHTMLにインラインで埋め込めるsymbol(sprite.symbol.svg)、2つのmodeのspriteを一度で生成。

・ 作業するディレクトリ(src)と出力されるディレクトリ(dest)をしっかり分けた。

・ アニメーションなどにはは使わないので余計な要素をsvgoのオプションで削除し、少しでも軽く。


## sprite.svgの使い方

### **sprite.view.svg**
background-imageで使う。
まずは生成された_svgSprite.scssを確認。

```scss:_svgSprite.scss
%svg-common {
	background: url("../dest/img/sprite.view.svg") no-repeat;
}

.svg-beer {
	@extend %svg-common;
	background-position: 0 0;
}

.svg-cat {
	@extend %svg-common;
	background-position: 100% 0;
}

```

わーい、自動でposition指定してくれてる！
が、backgroundのurlの相対パスがなんか違う。正しくはmain.cssからだから
```../img/sprite.view.svg```
であるべき。

いろいろ調べたけどパスの設定方法が見つからなかったので、自力で上書き。

```scss:_custom.scss
%svg-common {
  background-image: url("../img/sprite.view.svg"); // main.cssから見たパスに修正
  background-size: cover; // サイズはclassをつける要素で指定するのでとりあえず

```
_svgSprite.scssで生成されたclassをhtmlに追記すれば完成！
ただ、アイコンのためだけにhtmlを汚したくなかったので既存のclassにspriteの指定を継承する。

```html:index.html
      <div class="viewMode">
        <p class="viewMode-item cat">ねこです。</p>
        <p class="viewMode-item beer">ビールです。</p>
      </div> 
```

```scss:style.scss

.viewMode {
  display: flex;
  margin-top: 20px;
  &-item {
    padding: 10px 10px 10px 60px;
    border: 1px solid #ccc;
    position: relative;
    &:before {
      display: block;
      position: absolute;
      content: '';
      width: 40px;
      height: 40px;
      top: 50%;
      left: 10px;
      margin-top: -20px;
    }
    &.cat { 
      margin-right: 50px;
      &:before {
        @extend .svg-cat; //_svgSprite.scssから継承
      }
    }
    &.beer {
      &:before {
        @extend .svg-beer; //_svgSprite.scssから継承
      }
    }
  }
}
```
![bd55a26569e059fa459afaea871e6d8d.png](https://qiita-image-store.s3.amazonaws.com/0/125435/50398fcd-528b-73c3-5f17-9554ba09d2f8.png)


これで無事表示されました！



***
### **sprite.symbol.svg**
HTMLにタグを埋め込む。

``sprite.symbol.svg``からidを探して・・・。画像名である``id="beer"``と``id="cat"``を発見。
それをuseタグの``xlink:href``にファイルのパス+画像名+idを埋め込む。

```html:index.html
      <div class="symbolMode">
        <div class="symbolMode-item cat">
          <svg class="symbolMode-ico" role="img">
            <title>cat</title>
            <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="dest/img/sprite.symbol.svg#cat"></use>
          </svg>
          <p>ねこです。</p>
        </div>

        <div class="symbolMode-item beer">
          <svg class="symbolMode-ico" role="img">
            <title>beer</title>
            <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="dest/img/sprite.symbol.svg#beer"></use>
          </svg>
          <p>ビールです。</p>
        </div>
      </div> 
```


```scss:_style.scss
.symbolMode {
  display: flex;
  margin-top: 20px;
  &-item {
    display: flex;
    align-items: center;
    padding: 5px;
    border: 1px solid #ccc;
    border-radius: 5px;
    &.cat {
      fill: #8D4C32;
      margin-right: 50px;
    }
    &.beer {
      fill: #BF923B;
    }
  }
  &-ico {
    width: 40px;
    height: 40px;
    margin-right: 10px;
  }
}
```


![ee5e4928d46839e807c1ccb6fd5fc2fe.png](https://qiita-image-store.s3.amazonaws.com/0/125435/a2e8fb9c-ebaf-ed66-6b70-6f95d158c711.png)


出た！
``fill``で[ビールのカラーコード](http://brnavi.com/beer-color-code-chart.html)を指定してみた。


## IE対応
[svg4everybody](https://github.com/jonathantneal/svg4everybody)
こちらでカバー。
無事IE11/Edgeで見れた。


## 今後の課題

svgを一気にsprite化できて、HTMLでもCSSでも使えるのはかなり使い勝手が良い。
重い腰を上げてやってみてよかった！

ただ、心残りな点が2点。


**1. gulpfile.jsでsvg-spriteのオプション記述の部分だけ相対パスを1階層上に書かないと効かない。**
他のパッケージでは普通に``./``で動くのに・・・。 英語のドキュメントでは調査しきれなかった・・。とりあえず``../``から始めれば動くので一旦このまま。

**2.viewモードとsymbolモードでオプションが違う場合、taskを2回繰り返してしまう。**

```javascript:gulpfile.js
gulp.task('svg-sprite', () => {
  // 1回目: symbolモード
  gulp.src(imgDir)
    .pipe(svgSprite(symbol))
    .pipe(gulp.dest('.'));
 // 2回目: viewモード
  gulp.src(imgDir)
    .pipe(svgSprite(view))
    .pipe(gulp.dest('.'));
});
```
``gulp.src``と``gulp.dest``を1回にまとめたらエラーになってしまう。
とりあえず変数を変えた処理を2回書いたけど絶対イケてる良い書き方があるはず。

この2点はのちほど調査・・・。