[Qiita] gulpで複数のスプライト画像やWebフォントを管理する

gulpでスプライト画像や Webフォントを生成する

今や CSSスプライト画像や Webフォントを、手動で作成・更新する機会は少なくなりました。タスクランナーがファイルの生成から CSS定義まで自動でやってくれます。
自分は CSSスプライトには gulp.spritesmith、Webフォントには gulp-iconfont を愛用しています。
どちらも以下のように指定のディレクトリに格納されている複数の PNGファイル、SVGファイルを結合して 1つのスプライト画像やWebフォントを生成する仕様になっています。

# スプライト画像元データ
./develop/sprite/heart.png
./develop/sprite/diamond.png
./develop/sprite/spade.png
./develop/sprite/clover.png

# スプライト画像生成データ
./public/img/sprite_trump.png

# Webフォント元データ
./develop/font/twitter.svg
./develop/font/facebook.svg
./develop/font/googleplus.svg

# Webフォント生成データ
./public/font/sns.woff
./public/font/sns.ttf
./public/font/sns.eof

一度に複数のスプライト画像や Webフォントを管理したい

できるだけリソースは一つにまとめてしまった方がロードするファイル数も減り、表示速度も上るのでその方が好ましいのですが、

  • 利用用途別にファイルを分けたい
  • 作業担当者や公開時期が別なためファイルを分けたい
  • 一つのファイルに集約すると肥大化するのでカテゴリ別にファイルを分けたい
  • 既存コンテンツへの影響を最小限にするために別ファイルにしたい

などなど、敢えて結合ファイルを分けて管理したいケースがあります。
これを実現するためには、以下のようにディレクトリ名で出力ファイルがまとまるような形にします。

# スプライト画像元データ
./develop/sprite/trump/heart.png
./develop/sprite/trump/diamond.png
./develop/sprite/trump/spade.png
./develop/sprite/trump/clover.png
./develop/sprite/season/spring.png
./develop/sprite/season/summer.png
./develop/sprite/season/autum.png
./develop/sprite/season/winter.png

# スプライト画像生成データ
./public/img/sprite_trump.png
./public/img/sprite_season.png

# Webフォント元データ
./develop/font/sns/twitter.svg
./develop/font/sns/facebook.svg
./develop/font/sns/googleplus.svg
./develop/font/weather/sunny.svg
./develop/font/weather/rainy.svg
./develop/font/weather/cloudy.svg

# Webフォント生成データ
./public/font/sns.woff
./public/font/sns.ttf
./public/font/sns.eof
./public/font/weather.woff
./public/font/weather.ttf
./public/font/weather.eof

指定ディレクトリの子階層のディレクトリ名を取得する

Node.js 標準パッケージの fspath を使ってディレクトリ名を配列で取得します。

var fs   = require('fs');
var path = require('path');

var getFolders = function(dir_path) {
  return fs.readdirSync(dir_path).filter(function(file) {
    return fs.statSync(path.join(dir_path, file)).isDirectory();
  });
};

var folders = getFolders('./path/to/');
folders.forEach(function(folder){
  // タスク処理
}

処理部分でいつものスプライト画像や Webフォントのタスク処理に引き渡せばOKです。

実際に組み込む

以下、上記のディレクトリ取得を組み込んだ gulpfile.js の例です。
ディレクトリ名がそのままスプライト画像やフォントの名称となるため、タスク処理部分を変更することなく、ディレクトリを増やすだけで新たなスプライト画像やフォントを追加で生成できます。

'use strict';

// --------------------------
// Require
var fs          = require('fs');
var path        = require('path');
var gulp        = require('gulp');
var sprite      = require('gulp.spritesmith');
var iconfont    = require('gulp-iconfont');
var rename      = require('gulp-rename');
var consolidate = require('gulp-consolidate');

// --------------------------
// Functions
var getFolders = function(dir_path) {
  return fs.readdirSync(dir_path).filter(function(file) {
    return fs.statSync(path.join(dir_path, file)).isDirectory();
  });
};

// --------------------------
// Web Font Icon Create
gulp.task('iconfont', function() {
  var folders = getFolders('./develop/font/');
  folders.forEach(function(folder){
    gulp.src('./develop/font/' + folder + '/*.svg')
      .pipe(iconfont({
        fontName: folder,
        prependUnicode: true,
        startUnicode: 0xF001,
        formats: ['ttf', 'eot', 'woff'],
        normalize: true,
        fontHeight: 500
      }))
      .on('glyphs', function(glyphs, options) {
        var opt = {
          glyphs: glyphs.map(function(glyph) {
            return {
              name: glyph.name,
              codepoint: glyph.unicode[0].charCodeAt(0)
            };
          }),
          fontName: folder,
          fontPath: './font/',
          className: 'ico'
        };
        // Web Font CSS Sample Create
        gulp.src('./develop/template/iconfont-sample.css')
          .pipe(consolidate('lodash', opt))
          .pipe(rename({
            basename: folder
          }))
          .pipe(gulp.dest('./public/webfont'));
        // Web Font HTML Sample Create
        gulp.src('./develop/template/iconfont-sample.html')
          .pipe(consolidate('lodash', opt))
          .pipe(rename({
            basename: folder
          }))
          .pipe(gulp.dest('./public/webfont'));
      })
      .pipe(gulp.dest('./public/webfont/font'));
  });
});

// --------------------------
// CSS Sprite Create
gulp.task('sprite', function() {
  var folders = getFolders('./develop/img-sprite/');
  folders.forEach(function(folder){
    var spriteData = gulp.src('./develop/img-sprite/' + folder + '/*.png')
      .pipe(sprite({
        imgName: 'sprites_' + folder + '.png',
        imgPath: 'img/sprites_' + folder + '.png',
        cssName: folder + '.css',
        retinaSrcFilter: './develop/img-sprite/' + folder + '/*@2x.png',
        retinaImgName: 'sprites_' + folder + '@2x.png',
        retinaImgPath: 'img/sprites_' + folder + '@2x.png',
        padding: 4,
        cssTemplate: function(data) {
          var opt = {
            spriteName: folder,
            className: 'ico',
            data: data
          };
          // Sprite HTML Sample Create
          gulp.src('./develop/template/sprite-sample.html')
            .pipe(consolidate('lodash', opt))
            .pipe(rename({
              basename: folder
            }))
            .pipe(gulp.dest('./public/sprites'));
          // Sprite CSS Sample Create
          gulp.src('./develop/template/sprite-sample.css')
            .pipe(consolidate('lodash', opt))
            .pipe(rename({
              basename: folder
            }))
            .pipe(gulp.dest('./public/sprites'));
          return '';
        }
      }));
    spriteData.img
      .pipe(gulp.dest('./public/sprites/img'));
  })
});

データダウンロード

GitHub に実際にWebフォントやスプライト画像を作成できるサンプルデータを公開しました。
使い方は README を参照してください。
gulp task example: split by directory

※この記事はQiitaとのマルチポストになります。