これからはじめる Gulp シリーズこれからはじめるGulp(19):gulp-sketchとgulp-execを使ったSketch 3デザインデータの画像書き出し
はじめに
この記事はGulp.js(全俺) Advent Calendar 2014とSketch 3 Advent Calendar 2014の19日目です。前日のSketch 3 Advent Calendar 2014は@soutaroさんのヒラギノの縦位置を揃えるちょうべんりなSketchプラグインの紹介でした。2つのAdvent Calendarを掛け持ちしている記事でややこしくなってごめんなさい。Gulp.js(全俺) Advent Calendar 2014の方はnodebrewを使ったGulp.js環境の構築や使い方について一から紹介しているので興味があれば1日目から読んでみてください。
前回のGulp.js(全俺) Advent Calendar 2014ではこれからはじめるGulp(18):SketchToolで何ができるのかコマンドと主要なオプションを使ってみるでSketchToolの各コマンドとオプションを試して、Gulp.jsと連携するための下調べをしました。今回はgulp-sketchプラグインを使ったGulp.jsとSketchToolとの連携に加え、SketchToolで書き出す画像をページ毎に別々のフォルダに整理できるようにいくつかの方法を試してみたいと思います。
gulp-sketchプラグインを使ってみる
まずはgulp-sketchプラグインについてできること・できないことを確認しながらスライスの書き出しとgulp-imageminプラグインを使った画像の軽量化を行うタスクを作ってみます。
gulp-sketchはSketchToolをGulp.jsで使うためのプラグインです。一部のオプションには対応していませんが、SketchToolからエクスポートされる画像をStreamにのせることができます。パイプラインでそのままgulp-imagemin等の圧縮処理が行えgulp.dest()
で出力先を指定できます。
対応しているSketchToolオプション
gulp-sketchが対応しているSketchToolオプションは以下の通りです。SketchToolのオプションはこちらを参考にしてください。
- formats
- scales
- items
- bounds
- save-for-web
- compact
- trimmed
gulp-sketchプラグインのインストール
gulp-sketchプラグインをインストールします。
$ sudo npm install gulp-sketch --save-dev
Password:
gulp-sketch@0.0.6 node_modules/gulp-sketch
├── clean-sketch@1.0.1
├── temporary@0.0.8 (package@1.0.1)
└── through2@0.6.3 (xtend@4.0.0, readable-stream@1.0.33)
サンプルデザインファイル
以下のようなサンプルを用意しました。前回説明したとおり、SketchToolからエクスポートした画像のファイル名はレイヤー名そのままで出力されます。
sketchタスクを作る
gulp-sketchプラグインのインストールが完了したらGulpタスクを作ります。gulp-sketchとgulp-imageminの他にStream上で処理されているファイルを把握するためにgulp-filelogというプラグインを使います。
var gulp = require('gulp');
var sketch = require("gulp-sketch");
var imagemin = require('gulp-imagemin');
var filelog = require('gulp-filelog');
var paths = {
srcDir : 'src/assets/_design',
dstDir : 'src/assets/_design/exports',
}
gulp.task( 'sketchExport:slices', function(){
var srcGlob = paths.srcDir + '/example.sketch';
var dstGlob = paths.dstDir;
var sketchOptions = {
export : 'slices'
};
var imageminOptions = {
optimizationLevel: 7
};
return gulp.src( srcGlob )
.pipe(sketch( sketchOptions ))
.pipe(imagemin( imageminOptions ))
.pipe(gulp.dest( dstGlob ))
.pipe(filelog());
});
gulp.task( 'sketch', ['sketchExport:slices'] );
sketchタスクを試す
sketchタスクを試してみましょう。
$ gulp sketch
[02:18:19] Using gulpfile ~/Projects/gulp.whiskers.nukos.kitchen/gulpfile.js
[02:18:19] Starting 'sketch-export:slices'...
[02:18:19] [1] [/Users/nukos/Projects/gulp.whiskers.nukos.kitchen/src/assets/_design/exports/detail-mb-button_05x.png]
[02:18:19] [2] [/Users/nukos/Projects/gulp.whiskers.nukos.kitchen/src/assets/_design/exports/home-mb-button_05x.png]
[02:18:19] [3] [/Users/nukos/Projects/gulp.whiskers.nukos.kitchen/src/assets/_design/exports/detail-tablet-button_05x.png]
[02:18:19] [4] [/Users/nukos/Projects/gulp.whiskers.nukos.kitchen/src/assets/_design/exports/home-tablet-button_05x.png]
[02:18:19] [5] [/Users/nukos/Projects/gulp.whiskers.nukos.kitchen/src/assets/_design/exports/home-mb-button_1x.png]
[02:18:19] [6] [/Users/nukos/Projects/gulp.whiskers.nukos.kitchen/src/assets/_design/exports/detail-mb-button_1x.png]
[02:18:20] [7] [/Users/nukos/Projects/gulp.whiskers.nukos.kitchen/src/assets/_design/exports/detail-tablet-button_1x.png]
[02:18:20] gulp-imagemin: Minified 8 images (saved 11.44 kB - 46.3%)
[02:18:20] [8] [/Users/nukos/Projects/gulp.whiskers.nukos.kitchen/src/assets/_design/exports/home-tablet-button_1x.png]
[02:18:20] Found [8] files.
[02:18:20] Finished 'sketch-export:slices' after 1.01 s
[02:18:20] Starting 'sketch'...
[02:18:20] Finished 'sketch' after 7.3 μs
8つのスライスが書き出され、gulp-imageminで46.3%軽量化されました。 単純なデザインファイルであればgulp-sketchは使い勝手が良さそうです。
ページ毎に書き出し先のディレクトリを分けたい
ページやアートボード毎に出力先ディレクトリを分けたいと思いますよね。ですがSketchToolにその機能はなくコードで制御できません。代わりにSketch側でレイヤー名を/
で区切ることによりサブディレクトリへの書き出しが可能になります。この仕様はSketchTool側も対応しています。
例えば下記のようにスライス対象のレイヤー名をhome/mb/button
のようにすることでページ/アートボード/画像のようなディレクトリ構造を持たせた画像の書き出しが行えます。
このようにレイヤー名にディレクトリ構造を定義してエクスポートすると下記のようにサブディレクトリ作られ画像のファイルも末端の名前だけが付きます。
_design/exports
├── detail
│ ├── mb
│ │ ├── button_05x.png
│ │ └── button_1x.png
│ └── tablet
│ ├── button_05x.png
│ └── button_1x.png
└── home
├── button_05x.png
├── button_1x.png
├── mb
│ ├── button_05x.png
│ └── button_1x.png
└── tablet
├── button_05x.png
└── button_1x.png
とても便利な機能でエクスポート後の画像管理が一気に楽になります。 ただ、残念なことにgulp-sketchではこのサブディレクトリへの書き出しに対応していないためfsモジュールでエラーが起こり利用できません。
fs.js:487
var r = binding.read(fd, buffer, offset, length, position);
^
Error: EISDIR, illegal operation on a directory
at Object.fs.readSync (fs.js:487:19)
at Object.fs.readFileSync (fs.js:321:28)
at Socket.<anonymous> (/Users/nukos/Projects/test.io/vccw/www/wordpress/wp-content/themes/test.io/node_modules/gulp-sketch/index.js:78:20)
at Socket.emit (events.js:117:20)
at _stream_readable.js:943:16
at process._tickCallback (node.js:419:13)
これは仕方ないこととも思えます。というのも本来出力先の制御はGulp.js側で行うべきだからです。gulp-sketchを使う場合はGulp.jsの利点を活かし画像の名前をもとにファイルを整理するのも良いかもしれません。
方法1:ファイル名で振り分ける
例えば以下のようにプレフィックスを基準にページ・アートボード毎にディレクトリを定義しファイルをコピーするタスクを用意します。
var gulp = require('gulp');
var sketch = require('gulp-sketch');
var imagemin = require('gulp-imagemin');
var filelog = require('gulp-filelog');
var paths = {
srcDir : '_design',
dstDir : '_design/exports',
imgDir : 'assets/images/_src'
}
gulp.task( 'sketch:copy', ['sketch:slices'], function(){
var srcGlob = paths.dstDir;
var dstGlob = paths.imgDir;
var splitter = '_';
var pagePrefixs = {
'home' : ['mb', 'tablet'],
'detail' : ['mb', 'tablet']
};
for(var page in pagePrefixs) {
pagePrefixs[page].forEach(function( artboard ){
gulp.src( srcGlob + '/' + page + splitter + artboard + splitter + '*' )
.pipe(gulp.dest( dstGlob + '/' + page + '/' + artboard ))
.pipe(filelog());
});
}
});
こうすることで画像のファイル名にプレフィックスは残ってしまいますがgulp-sketchを使っていてもページ・アートボードでディレクトリを分けることができます。
_design/exports
├── detail
│ ├── mb
│ │ ├── detail_mb_button_05x.png
│ │ └── detail_mb_button_1x.png
│ └── tablet
│ ├── detail_tablet_button_05x.png
│ └── detail_tablet_button_1x.png
└── home
├── mb
│ ├── home_mb_button_05x.png
│ └── home_mb_button_1x.png
└── tablet
├── home_tablet_button_05x.png
└── home_tablet_button_1x.png
方法2:–outputJSONオプションを活用して振り分ける
--outputJSON
オプションを使って出力したjsonファイルはページとスライスの情報を含んでいます。このjsonファイルにはアートボードの情報がないのでjsonの情報だけをベースに振り分けられるのはページ単位のみです。jsonを使うメリットは振り分けるためのページ名をjsonから取得できるので大量のページを持ったSketchファイルなどの場合に都度ページ名を変更せずに済みます。
{
"home" : {
"home_tablet_button" : {
"home_tablet_button_05x.png" : {
"format" : "png",
"height" : 129,
"x" : 3421,
"y" : 973,
"width" : 533,
"name" : "home_tablet_button_05x",
"scale" : 0.5
},
"home_tablet_button_1x.png" : {
"format" : "png",
"height" : 129,
"x" : 3421,
"y" : 973,
"width" : 533,
"name" : "home_tablet_button_1x",
"scale" : 1
}
}
}
}
}
エクスポートとコピータスク
タスクにはSketchToolとgulp-execプラグインを使ったエクスポート用タスクとjsonを元にディレクトリを分けるコピータスクを作ります。jsonファイルはfsモジュールを使ってコピータスクに読み込みます。
var gulp = require('gulp');
var fs = require('fs');
var exec = require('gulp-exec');
var filelog = require('gulp-filelog');
var paths = {
srcDir : '_design',
dstDir : '_design/exports'
}
//exports task
gulp.task( 'sketch:exports', function(){
var srcGlob = paths.srcDir + '/example.sketch';
var dstGlob = paths.dstDir;
var exportOptions = {
dstDir : dstGlob,
jsonFile : '/slices.json'
};
var sketchtool = 'sketchtool export slices <%= file.path %> --output=<%= options.dstDir %> --outputJSON=<%= options.dstDir %><%= options.jsonFile %>';
return gulp.src( srcGlob )
.pipe(exec( sketchtool, exportOptions ))
.pipe(exec.reporter());
});
//copy task
gulp.task( 'sketch:copy', ['sketch:exports'], function(){
var sketchFile = 'example.sketch';
var srcGlob = paths.dstDir;
var dstGlob = paths.dstDir;
var jsonPath = './' + paths.dstDir + '/slices.json';
var exportJson = JSON.parse(fs.readFileSync( jsonPath, 'utf8' ));
var pages = exportJson[sketchFile];
for(var page in pages) {
for (var slice in pages[page]){
for (var item in pages[page][slice]){
gulp.src( srcGlob + '/' + pages[page][slice][item].name + '.' + pages[page][slice][item].format )
.pipe(gulp.dest( dstGlob + '/' + page ))
.pipe(filelog());
}
}
}
});
実際にタスクを実行するとこのように処理されcopyタスクでStreamを使った処理が行えます。
gulp sketch:copy
[17:07:12] Using gulpfile ~/Projects/marke.io/vccw/www/wordpress/wp-content/themes/marke.io/gulpfile.js
[17:07:12] Starting 'sketch:exports'...
[17:07:13] Exported home_mb_button_1x.png
Exported home_mb_button_05x.png
Exported home_tablet_button_1x.png
Exported home_tablet_button_05x.png
Exported detail_mb_button_1x.png
Exported detail_mb_button_05x.png
Exported detail_tablet_button_1x.png
Exported detail_tablet_button_05x.png
[17:07:13] Finished 'sketch:exports' after 163 ms
[17:07:13] Starting 'sketch:copy'...
[17:07:13] Finished 'sketch:copy' after 15 ms
[17:07:13] [1] [/Users/nukos/Projects/marke.io/vccw/www/wordpress/wp-content/themes/test.io/_design/exports/detail/detail_mb_button_1x.png]
[17:07:13] Found [1] files.
[17:07:13] [1] [/Users/nukos/Projects/marke.io/vccw/www/wordpress/wp-content/themes/test.io/_design/exports/detail/detail_mb_button_05x.png]
[17:07:13] Found [1] files.
[17:07:13] [1] [/Users/nukos/Projects/marke.io/vccw/www/wordpress/wp-content/themes/test.io/_design/exports/home/home_mb_button_1x.png]
[17:07:13] Found [1] files.
[17:07:13] [1] [/Users/nukos/Projects/marke.io/vccw/www/wordpress/wp-content/themes/test.io/_design/exports/detail/detail_tablet_button_05x.png]
[17:07:13] Found [1] files.
[17:07:13] [1] [/Users/nukos/Projects/marke.io/vccw/www/wordpress/wp-content/themes/test.io/_design/exports/detail/detail_tablet_button_1x.png]
[17:07:13] Found [1] files.
[17:07:13] [1] [/Users/nukos/Projects/marke.io/vccw/www/wordpress/wp-content/themes/test.io/_design/exports/home/home_tablet_button_05x.png]
[17:07:13] Found [1] files.
[17:07:13] [1] [/Users/nukos/Projects/marke.io/vccw/www/wordpress/wp-content/themes/test.io/_design/exports/home/home_mb_button_05x.png]
[17:07:13] Found [1] files.
[17:07:13] [1] [/Users/nukos/Projects/marke.io/vccw/www/wordpress/wp-content/themes/test.io/_design/exports/home/home_tablet_button_1x.png]
[17:07:13] Found [1] files.
--outputJSON
で書き出すjsonはjekyll等のテンプレートエンジンで活用することで、デザインデータを編集してGulpタスクを実行するだけ静的なサイトを更新したりできそうですね。
方法3:Sketchのレイヤー名で振り分ける
最終的にこの方法が一番無駄がなくファイル名もシンプルです。デザインデータのスライス名をhome/mb/button
のように書き換えます。ちなみにサブディレクトリに書き出せるのはスライスだけではなくアートボードでも可能です。
タスク
var gulp = require('gulp');
var exec = require('gulp-exec');
var paths = {
srcDir : '_design',
dstDir : '_design/exports',
}
gulp.task( 'sketch:exports', function(){
var srcGlob = paths.srcDir + '/example.sketch';
var dstGlob = paths.dstDir;
var exportOptions = {
dstDir : dstGlob,
jsonFile : '/slices.json'
};
var sketchtool = 'sketchtool export slices <%= file.path %> --output=<%= options.dstDir %> --outputJSON=<%= options.dstDir %><%= options.jsonFile %>';
return gulp.src( srcGlob )
.pipe(exec( sketchtool, exportOptions ))
.pipe(exec.reporter());
});
エクスポートされた画像
_design/exports
├── detail
│ ├── mb
│ │ ├── button_05x.png
│ │ └── button_1x.png
│ └── tablet
│ ├── button_05x.png
│ └── button_1x.png
├── home
│ ├── mb
│ │ ├── button_05x.png
│ │ └── button_1x.png
│ └── tablet
│ ├── button_05x.png
│ └── button_1x.png
└── slices.json
これで望ましいかたちで画像を書き出すことができました。あとは圧縮用のタスク等を用意してあげればデザインデータからのスライス書き出しを自動化できます。欲を言うとSketchToolのexportコマンドでページ・アートボードを制限するオプションがあると便利ですね。あとはgulp-sketchもレイヤー名のサブディレクトリ指定を上手く扱ってくれればなぁといったところですね。
以上、SketchToolを使ったスライスの書き出しとディレクトリの振り分けでした。 Sketch 3 Advent Calendar 2014の20日目は@kgsiさんです。よろしくお願いします!
シリーズ
- これからはじめるGulp(0):アドベントカレンダースケジュール
- これからはじめるGulp(1):bundler, rbenv, nodebrewでGulp環境を作ってみる
- これからはじめるGulp(2):gulp-sassを使ってSCSSをコンパイルするタスクを作ってみる
- これからはじめるGulp(3):gulp.watchでファイルの変更を監視しタスクを実行する
- これからはじめるGulp(4):gulp-connectモジュールを使ったローカルサーバの起動
- これからはじめるGulp(5):gulp-connectモジュールを使ったLiveReload
- これからはじめるGulp(6):gulp-plumberとgulp-notifyを使ったデスクトップ通知
- これからはじめるGulp(7):require-dirモジュールを使ったタスク単位のファイル分割
- これからはじめるGulp(8):delモジュールとvinyl-pathsモジュールを使ったファイルの削除
- これからはじめるGulp(9):Ruby版Sassが使えるgulp-ruby-sassへの乗り換え
- これからはじめるGulp(10):deprecatedになっていたgulp-connectからgulp-webserverへ乗り換える
- これからはじめるGulp(11):ブラウザ間でスクロールやクリック操作を同期できるBrowserSync
- これからはじめるGulp(12):gulp-imageminプラグインを使ったJPEG,PNG,GIF,SVGの最適化
- これからはじめるGulp(13):gulp-changedプラグインで変更されたファイルだけを処理させる
- これからはじめるGulp(14):gulp-cachedプラグインで変更されたSCSSファイルだけを処理させる
- これからはじめるGulp(15):gulp-responsiveプラグインを使ったレスポンシブイメージ作成の自動化
- これからはじめるGulp(16):gulp-image-resizeプラグインを使ってサムネイルやレスポンシブイメージを作る
- これからはじめるGulp(17):SketchTool(Sketch 3 command line tool)を使ったアートボード・スライスの書き出しとgulp-execプラグインを使ったSketchtoolの呼び出し
- これからはじめるGulp(18):SketchToolで何ができるのかコマンドと主要なオプションを使ってみる
- これからはじめるGulp(19):gulp-sketchとgulp-execを使ったSketch 3デザインデータの画像書き出し
- これからはじめるGulp(20):Node.jsのChild Processモジュールを使ってgulpからjekyllのbuildコマンドを実行する
- これからはじめるGulp(21):gulp-awspublishプラグインを使ったAmazon S3への静的Webサイトパブリッシュ
- これからはじめるGulp(22):gulp-iconfontとgulp-sketchを使ったアイコンフォント作成の自動化
- これからはじめるGulp(23):gulp-consolidateでgulp-iconfontで作ったアイコンフォントのシンボル一覧HTMLを作る
- これからはじめるGulp(24):gulp.spritesmithプラグインでSpriteイメージを作る
- これからはじめるGulp(25):Hologramとgulp-hologramでスタイルガイドを作る
- これからはじめるGulp(26):Sketch3のサブディレクトリ書き出しに対応したgulp-sketchを試す