出口 (@dex1t) です。 ベクター素材をSVGスプライト化し、インラインSVGとして利用する仕組みを作ったので紹介します。
アイコンフォントかSVGか
アイコンフォントとSVGの違いは、ググるとたくさん出てくると思うのですが、
- 現在開発中のサービスはモダンブラウザのみ対象にしている
- SVGのまま利用すれば、フォント化する手間がなく管理もしやすい
- 多色にも対応できる
という点から、今回はSVGを利用することにしました。 SVGを利用する方法もいくつかありますが、今回はSVGをスプライト化し、Inline SVGとしてHTMLに埋め込む形を取ることにしました。
デザイナーにSVGを追加更新してもらいたい
いまはサービスをガンガン開発してる段階なので、素材の追加や変更も多く発生します。 ベクター素材は、デザイナーがイラレ上で作っているので、書き出しからSVGスプライト化まで一貫してお願いできると、作業効率が上がりそうです。
いま一緒に作業しているデザイナーさんは、黒い画面がある程度使える方なので、 規定のディレクトリにSVG素材を入れてもらい、コマンドを一発叩くとSVGスプライトが生成されるような環境を作ります。
GulpでSVGスプライトを生成する
タスクランナーとして、この手のプラグインが充実しているGulpを利用します。 今回は以下のGulpプラグインを組み合わせました。
- gulp-svgstore
- svgのスプライト化
- gulp-svgmin
- gulp-cheerio
- HTMLの整形
こんな感じの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のような既存のテンプレートを流用してもよさそうです。
便利ヘルパーと合わせてGem化する
今回はRailsアプリで利用するので、これらを社内Gem (Rails Engine) 化しました。 Gem化することで、アプリ側からはバージョン管理ができて便利です。 Inline SVGは、アイコンフォントに比べて若干使う際のマークアップが面倒なので、Rails向けの簡単なヘルパーも用意しています。
まとめ
このような仕組みを作ることで、以下のフローをデザイナーにお願いできるようになりました。
エンジニアは、Railsアプリ側でGemをアップデートするだけで、最新版の素材が利用できます。 素材周りは全てデザイナーにお任せできるので開発スピードがあがりそうです 🎉