GulpでSVGスプライトとアイコン一覧を一発生成

出口 (@dex1t) です。 ベクター素材をSVGスプライト化し、インラインSVGとして利用する仕組みを作ったので紹介します。

アイコンフォントかSVG

アイコンフォントとSVGの違いは、ググるとたくさん出てくると思うのですが、

  • 現在開発中のサービスはモダンブラウザのみ対象にしている
  • SVGのまま利用すれば、フォント化する手間がなく管理もしやすい
  • 多色にも対応できる

という点から、今回はSVGを利用することにしました。 SVGを利用する方法もいくつかありますが、今回はSVGをスプライト化し、Inline SVGとしてHTMLに埋め込む形を取ることにしました。

デザイナーにSVGを追加更新してもらいたい

いまはサービスをガンガン開発してる段階なので、素材の追加や変更も多く発生します。 ベクター素材は、デザイナーがイラレ上で作っているので、書き出しからSVGスプライト化まで一貫してお願いできると、作業効率が上がりそうです。

いま一緒に作業しているデザイナーさんは、黒い画面がある程度使える方なので、 規定のディレクトリにSVG素材を入れてもらい、コマンドを一発叩くとSVGスプライトが生成されるような環境を作ります。

GulpでSVGスプライトを生成する

スクランナーとして、この手のプラグインが充実しているGulpを利用します。 今回は以下のGulpプラグインを組み合わせました。

こんな感じのgulpタスクを書くと、一発で生成できます。 なお、プラグインを毎度requireするのは大変なので、gulp-load-pluginsも使っています。

var gulp = require('gulp'),
    gulpLoadPlugins = require('gulp-load-plugins'),
    pl = gulpLoadPlugins();

gulp.task('build', function() {
  return gulp.src('src/*.svg')
    .pipe(pl.svgmin())
    .pipe(pl.svgstore({ inlineSvg: true }))
    .pipe(pl.cheerio(function ($) {
      $('svg').attr({
        style:  'position: absolute; width: 0; height: 0;',
        width: 0,
        height: 0
      });
    }))
    .pipe(gulp.dest('app/assets/'));
});

後述しますが、生成したSVGスプライトをGem化して管理しているため、生成先をapp/assets/配下にしています。

ついでにアイコン一覧用の静的HTMLも生成する

SVGスプライトを生成するだけでなく、SVGアイコンの一覧 (例: Material Icons) を生成すると開発時に便利です。 前述した、gulp-cheerioと、gulp-templateを組み合わせて、これも自動生成します。

このようなテンプレートを用意した上で、

<html>
  <head>
    <title>Bit Journey Symbols</title>
  </head>
  <body>
    <%= inlineSvg %>
    <h1 class='title'>Bit Journey Symbols</h1>
    <ul class='symbols'>
      <% _.each(symbols, function(symbol) { %>
        <li class='symbol'>
          <svg><use xlink:href="#<%= symbol.id %>"></use></svg>
          <div class='symbol-id'><%= symbol.id %></div>
        </li>
      <% }); %>
    </ul>
  </body>
</html>

以下の様なgulpタスクで、テンプレートにSVGメタデータを流し込み、SVGスプライトとアイコン一覧ページを同時生成します。

var gulp = require('gulp'),
    gulpLoadPlugins = require('gulp-load-plugins'),
    pl = gulpLoadPlugins(),
    templateFile = 'template.html';

gulp.task('build', function() {
  return gulp.src('src/*.svg')
    .pipe(pl.svgmin())
    .pipe(pl.svgstore({ inlineSvg: true }))
    .pipe(pl.cheerio(function ($) {
      $('svg').attr({
        style:  'position: absolute; width: 0; height: 0;',
        width: 0,
        height: 0
      });

      var symbols = $('svg > symbol').map(function (){
        return {
          id: $(this).attr('id'),
        };
      }).get();

      gulp.src(templateFile)
        .pipe(pl.template({
          inlineSvg: $('svg'),
          symbols: symbols
        }))
        .pipe(gulp.dest('public'));
    }))
    .pipe(gulp.dest('app/assets/'));
});

実際に作った一覧ページはこんな感じです。今回は適当にCSSを書きましたが、symbols-for-sketchのような既存のテンプレートを流用してもよさそうです。

f:id:dex1t:20150804180108p:plain

便利ヘルパーと合わせてGem化する

今回はRailsアプリで利用するので、これらを社内Gem (Rails Engine) 化しました。 Gem化することで、アプリ側からはバージョン管理ができて便利です。 Inline SVGは、アイコンフォントに比べて若干使う際のマークアップが面倒なので、Rails向けの簡単なヘルパーも用意しています。

まとめ

このような仕組みを作ることで、以下のフローをデザイナーにお願いできるようになりました。

  1. イラレからSVGを書き出す
  2. gulpコマンドを実行
  3. SVGスプライトと一緒に生成されるアイコン一覧ページで表示を確認
  4. プルリクエストを送ってもらう

エンジニアは、Railsアプリ側でGemをアップデートするだけで、最新版の素材が利用できます。 素材周りは全てデザイナーにお任せできるので開発スピードがあがりそうです 🎉