UNORTHODOX WORKBOOK

BLOG TOPFront-EndGulpでSVGスプライトを自動生成する

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

catch-svg-sprite-generator-with-gulp

前回のエントリーでSVGスプライトを非同期で読み込む方法を紹介しましたが、今回はSVGスプライトをGulpで自動生成する方法を紹介します。

ここで生成するSVGスプライトは、HTMLにuseタグを使ってインラインで表示させるもので、CSSのbackgroundでは表示させることができませんのでご了承ください。

なお、インライン用のSVGスプライトの場合、(色情報等を削除するため)目視で確認することが難しくなってしまうので、SVGスプライトの生成と同時に一覧表示させるためのHTMLも自動で生成できるようにしています。

Githubリポジトリにすべてのコードをあげていますので、以下のリンクからダウンロードすることができます(構成やディレクトリ等、ここで記載しているものとは若干異なります)。

https://github.com/42EG4M1/svg-sprite-generator

gulpの環境

まずはgulpの環境やタスク、ディレクトリ等の構成を記載しておきます。

プラグイン

使用するプラグインは以下の通りです。

gulp-svgmin
SVGを圧縮するプラグイン。改行やコメントアウト、不要な属性等を削除。
gulp-svgstore
複数のSVGファイルを一つにまとめるプラグイン。各SVGファイルのsvgタグをsymbolに置き換え、ファイル名をidにセット。
gulp-cheerio
HTMLやXMLを書き換えるプラグイン。jQueryライクに操作できる。余分な要素や属性を削除し、リストHTML生成時に使用する変数を定義。
gulp-template
静的HTMLを生成するプラグイン。gulp-cheerioと合わせて使用する。事前に用意しておいたHTMLへデータ(SVG)を流し込む。
gulp-rename
ファイル名を変更するプラグイン。

記載していませんが、「gulp-」ではじまるgulpのプラグインを読み込む際には、gulp-load-plugins を入れておくと便利です。

タスク

タスクは以下のような感じになります。基本的に、Illustratorで作成したSVGを対象としています。

【追記】
gulp-cheerioをそのまま使用するとviewBoxが全て小文字(viewbox)に変換されてしまい、HTML上でSVGがうまく表示されない不具合がありました。gulp-cheerioにオプションを指定することで回避できるため、以下赤文字部分を追加し訂正いたしました。

gulpfile.js

var gulp     = require('gulp');
var svgmin   = require('gulp-svgmin');
var svgstore = require('gulp-svgstore');
var cheerio  = require('gulp-cheerio');
var temp     = require('gulp-template');
var rename   = require('gulp-rename');

gulp.task('svg', function() {
  gulp.src('./src/images/svg/*.svg')
  .pipe(svgmin())
  .pipe(svgstore({ inlineSvg: true }))
  .pipe(cheerio({
    run: function($, file) {
      // 不要なタグを削除
      $('style,title,defs').remove();
      // symbolタグ以外のid属性を削除
      $('[id]:not(symbol)').removeAttr('id');
      // Illustratorで付与される「st」または「cls」ではじまるclass属性を削除
      $('[class^="st"],[class^="cls"]').removeAttr('class');
      // svgタグ以外のstyle属性を削除
      $('[style]:not(svg)').removeAttr('style');
      // data-name属性を削除
      $('[data-name]').removeAttr('data-name');
      // fill属性を削除
      $('[fill]').removeAttr('fill');
      // svgタグにdisplay:noneを付与(読み込み時、スプライト全体を非表示にするため)
      $('svg').attr({
        style: 'display:none'
      });

      // _base.htmlに渡すid
      var symbols = $('svg > symbol').map(function() {
        return {
          id: $(this).attr('id')
        };
      }).get();

      // _base.htmlを基に、_sample.htmlをルートに生成
      gulp.src('./src/images/svg/_base.html')
      .pipe(temp({
        inlineSvg: $('svg'),
        symbols: symbols
      }))
      .pipe(rename('_sample.html'))
      .pipe(gulp.dest('./'));
    },
    parserOptions: {
      xmlMode: true
    }
  }))
  .pipe(rename('sprite.min.svg'))
  .pipe(gulp.dest('./dist/images/'));
});

cheerioの辺りがごちゃごちゃしてますが、svgminで削除されない不要な要素等を削除しつつ、SVGスプライトを一覧表示するための設定とかを行っています。

不要な要素や属性の削除は、Illustratorで作成されたSVGに合わせているため、他のツールで作成されたSVGの場合には、それに合わせた書き方が必要になる場合があります。
なお、Illustratorで作成した場合の保存方法は、どのような形でも大丈夫なようにしているつもりですが、バージョンによっても出力結果が異なるので、以下「生成手順」にて推奨の保存方法を記載しています。

gulp-cheerio とか gulp-template は情報が少なくて、もしかするともっと良い書き方があるかもしれません。gulp-template は、テンプレートエンジンみたいな感じで、EJSのように書くことができるようです(以下参照)。

HTMLテンプレート

SVGスプライトを一覧表示するために、HTMLのテンプレートを用意しておきます。

_base.html

<html>
<head>
  <title>SVG Sprite List</title>
  <style>
    /*省略*/
  </style>
</head>
<body>
  <div class="container">
    <%= inlineSvg %>
    <h1 class="title">SVG Sprite List</h1>
    <div class="items">
      <% _.each(symbols, function(symbol) { %>
        <div class="items__inner">
          <svg><use xlink:href="#<%= symbol.id %>"></use></svg>
          <p class='items__id'><%= symbol.id %></p>
        </div>
      <% }); %>
    </div>
  </div>
</body>
</html>

<%=%>で、gulpfile.jsのtempで定義した変数を使用することができます。<%%>のデリミタでeachif等も利用することができるようです。

gulpfile.js を見ると分かると思いますが、上記 _base.html を基に、タスクの実行後、SVGスプライトのリストを _sample.html と名前を変えて新たに生成させるような仕組みになっています。ルートに生成させていますが、これは好みで変更してください。

ディレクトリ

ディレクトリ構成は以下のような感じになっています。

/
├ src
│ └ images
│   └ svg
│     ├ _base.html
│     ├ example1.svg
│     ├ example2.svg
│     └ ...
│
├ dist
│ └ images
│   └ sprite.min.svg
│
├ _sample.html
│
├ ...

赤文字部分がタスクの実行で生成されるディレクトリやファイルです。

スプライトにしたいSVGファイルは、./src/images/svg/に置いていきます。

SVGスプライトの生成手順

この機能を使って、実際にSVGスプライトを生成する方法を簡単に説明します。

IllustratorでSVGファイルを作成

まずは肝心のSVGファイルの作成ですが、すでに記載している通りAdobe Illustrator CCでの作成を推奨しています。CCであれば問題ないと思いますが、今回使用したのは CC 2015.3 です。

IllustratorでSVGを作成(保存)する場合、幾つか方法があり、出力結果が微妙に異なります。基本的にはどの保存方法でも対応できるようにしています(つもりです)が、推奨の保存方法は、

  1. Illustrator上で対象となる画像を選択しcommand+cでコピー
  2. エディタで新規ファイルを作成しcommand+vで貼り付け
  3. 最終行に空白行がある場合は削除し、ファイル名(半角英数.svg)をつけて保存

です。最終空白行の削除の必要性は、使用するエディタによって変わってくるかもしれませんが、空白行がある場合は念のため削除します(削除しないとタスク実行時に「Error: Error in parsing SVG: Text data outside of root node.」というエラーが出る場合があります)。

ファイル名は、そのままsymbolタグに付与されるidとなり、表示させる時に使用することになるので、なるべく分りやすいものがいいと思います。

svgフォルダにまとめてビルド

作成したSVGファイルは全て./src/images/svg/の中に置き、良きところでタスクを実行します。

$ gulp svg

タスクを実行すると、./dist/images/sprite.min.svgというSVGスプライトファイルと、ルートに_sample.htmlという一覧(以下)が生成されます。

svg-sprite-ist

各画像(アイコン)の下に記載されているのがidになります。これを、表示させたいところでuseタグのxlinkにセットし表示させます。

<svg class="tw-icon"><use xlink:href="#twitterIcon"/></svg>

以上で完了です。

2色以上の画像をスプライトにしたい場合

設置が完了した後はCSSで色を指定することになるのですが、上記の方法だと1画像につき1色しか使うことができません。

もし、1つの画像に対して2色以上使いたい場合は、(スプライト前の)元SVGファイルの対象となる要素に対して個別にclassをつけて、CSSでそれぞれ色を指定する必要があります。classをつける要素はg要素でもpath要素でもcircle要素でも何でも大丈夫です。

元SVGファイルでは、「st」または「cls」で始まるclass属性は削除されてしまいますが、それ以外のclass属性はスプライト後も残りますので、個別に設定することができます。

色はCSSで管理したいというのがあって、それを維持したままうまく自動化する方法が見つからなかったため少し面倒になってしまいますが、2色以上使いたい場合は、上記のような対応をとっていただければと思います。

1色だけの単純なアイコンで使う場合には特に問題になることはないです。

まとめ

ずっとSVGの(自分なりの)効率的な使用方法を模索してきて、個人的には前回のエントリーと合わせてこの方法がしっくりきています。テスト的にはもちろん使っていますが、実際のプロジェクトではまだ組み込んだことはないので、使用して何かあればまた追記したいと思います。

使ってみて何か変なところがあればコメント等で教えていただけると助かります。


以下のサイトを参考にさせていただきました。
Gulpを使ったSVGスプライトのアイコンシステムとワークフローの作り方
GulpでSVGスプライトとアイコン一覧を一発生成

ABOUT

it's me

長野県北部を拠点にフリーランスとして活動しています。
Webサイトの制作をメインに、グラフィックデザインなどの制作も行っています。 Twitter / GitHub / About

PAGE TOP