日々進化している最近のコーディング (フロントエンド開発) 環境について、
ここ1年くらいのノウハウを詰めて、
極端には新しいもの好きではない視点で整理します。
目次:
1. psdからのスライス
2. Compass
3. スプライトイメージの自動化
4. CoffeeScript
5. Gruntによる自動化
6. ローカルサーバー
7. その他
1. psdからのスライス
Slicyというアプリを使い始めてからは、ずっとこれです。
Photoshopのレイヤーやグループ名に画像のファイル名(+拡張子)を付けることで、
ワンクリックで画像を書き出せます。
ファイル名に@2xと付けることで、Retina用画像(@2x)と、
等倍画像を同時に書き出すこともできます。
その他に@boundsというレイヤーをつくって矩形領域を指定したり、
@slicesというフォルダをつくって、その中にファイル名と
領域指定用のレイヤーをつくることも出来ます。
公式サイトのヘルプを読んだり、
サンプルをダウンロードすると理解が早いです。
2. Compass
CSSにネスト構造をつくって記述できるフレームワーク。
インストールの方法などは公式ドキュメント参照。
このエントリーでは、”gem install compass –pre” でインストール出来る、
Compass 1.0.0.rc.1を使って、SCSSの文法で書いています。
必要なコマンドは
compass watch
3. スプライトイメージの自動化
Compassのmixinを使えば、関数や変数を使ってCSSを記述できます。
一番の恩恵として、スプライトイメージの生成の自動化について、
色々試行錯誤した結果、現時点で使っているmixinを紹介します。
参考: Using Compass Generated Sprite Sheets in Responsive Sass, by Jayson Jacobs.
・サーバーにアップするファイルはassetsフォルダにまとめる
・サーバーにアップしないファイルはsourceフォルダにまとめる
・Source Mapを有効にするとChromeでscssファイルのデバッグができる
参考: Working with CSS Preprocessors – Google Chrome
・ロゴ用の画像として、
/resource/images/sprite/logo.png
/resource/images/sprite_2x/logo_2x.png (2倍サイズ)
の2枚が用意されているとする。
− config.rb
css_dir = "assets/css" sass_dir = "source/sass" images_dir = "source/images" generated_images_dir = "assets/images" javascripts_dir = "assets/js" fonts_dir = "assets/fonts" output_style = :nested relative_assets = true line_comments = true preferred_syntax = :scss sass_options = { :sourcemap => true }
− HTML
<div class="logo"> <div class="sprite">Logo</div> </div>
→ .sprite部分に、background-imageが適用される
− scssファイル
// スプライト画像 .sprite { display: block; overflow: hidden; background-repeat: no-repeat; text-align: left; text-indent: -10000px; font-size: 1px; line-height: 1px; } @mixin sprite-background($sprites, $sprites-img, $name, $scale, $skip-base-params:false) { background-image: $sprites-img; @include background-size(image-width(sprite-path($sprites)) / $scale auto); //retina時に重複するパラメータをスキップするための分岐 @if not($skip-base-params){ height: image-height(sprite-file($sprites, $name)) / $scale; width: image-width(sprite-file($sprites, $name)) / $scale; $ypos: nth(sprite-position($sprites, $name), 2) / $scale; background-position: 0 $ypos; } } $sprites: sprite-map("sprite/*.png", $spacing: 2px); $sprites-img: sprite-url($sprites); $sprites_2x: sprite-map("sprite_2x/*.png", $spacing: 4px); $sprites-img_2x: sprite-url($sprites_2x); @mixin sprite-image ($name) { .sprite { @include sprite-background($sprites, $sprites-img, $name, 1); } .backgroundsize.retina_2x & { .sprite { @include sprite-background($sprites_2x, $sprites-img_2x, $name, 2, true); } } }
→ /assets/images/ フォルダ内にスプライトイメージが書き出される。
また、Slicyからの作業の効率化を考えて、一つ、自作のRubyのスクリプトを使っています。
ruby mv_sprite.rb
で実行すると、実行したディレクトリ以下の@2xという名前がついた画像を、
_2xにリネームして、さらに_2x付きのディレクトリを作ってその中へ移動します。
@がついたファイル名が、Compassではコンパイルエラーを起こすことへの対処です。
(Compass 0.13.alpha.12時点)
上記mixinも、Slicy書き出し→このRubyの処理、が前提になっています。
例:
assets/images/sprite/logo.png
assets/images/sprite/logo@2x.png
↓
assets/images/sprite/logo.png
assets/images/sprite_2x/logo_2x.png
– mv_sprite.rb
# ディレクトリを再帰的に抽出する require 'pp' require 'FileUtils' root = File.expand_path(File.dirname(__FILE__)) root = ARGV[0] if ARGV[0] # フォルダの一覧を取得 dir_entries = Dir.glob(root + "/" + "**/*") # pp dir_entries for filePath in dir_entries do # pp file ext = File::extname(filePath) # 拡張子取得 # 画像のみに処理 if( ext == '.jpg' || ext == '.png' || ext == '.gif' ) dir = File::dirname(filePath) # ディレクトリ base = File::basename(filePath,ext) # 拡張子除去 twox_index = base.index('@2x') # @2xを含む if( twox_index && twox_index >= 0 ) p "#{dir}/#{base}#{ext}" parent_dir = File::dirname(dir) # e.g. path_to_htdocs/common/img my_dir = File::basename(dir) # e.g. sprite twox_my_dir = "#{my_dir}_2x" # e.g. sprite_2x twox_dir = "#{parent_dir}/#{twox_my_dir}" # e.g. path_to_htdocs/common/img/sprite_2x twox_base_ext = "#{base.sub('@2x', '_2x')}#{ext}" # e.g. hoge_2x.png # ディレクトリがなければ作る FileUtils.mkdir_p(twox_dir) unless FileTest.exist?(twox_dir) # ファイルを移動する File.rename filePath, "#{twox_dir}/#{twox_base_ext}" p "#{twox_dir}/#{twox_base_ext}" p '--' end end end
4. CoffeeScript
JavaScripterの間ではすっかり浸透したCoffeeScript を使います。
コードの行数、文字数が減らせるので、その分コメントを充実させられます。
必要なコマンドは
coffee -o assets/js -cw assets/coffee
ここまでの内容をまとめたものは、
https://github.com/soohei/compass-coffee-template
にあります。
5. Gruntによる自動化
注) 最新の状況はこちら
Gruntはこれまでに書いた、CompassやCoffeeScriptのコンパイル処理、
ソースの結合やMinify、ファイルの監視、簡易サーバーの起動などを
全て一つのコマンドで実行してしまう脅威の仕組みです。
他にGulpというのがあるようなのですが、まだ試していないです..
セットアップ方法は省略します。普段使っている基本的な設定・プラグインの構成は以下。
– package.json (使用しているパッケージの一覧)
{ "name": "sample", "version": "0.0.0", "author": "Sohei Kitada", "devDependencies": { "grunt": "~0.4.5", // ベンダープレフィックス補完 "grunt-autoprefixer": "~1.0.0", // メディアクエリーをまとめる "grunt-combine-media-queries": "~1.0.19", // CoffeeScript "grunt-contrib-coffee": "~0.11.0", // Compass "grunt-contrib-compass": "~0.9.1", // ファイルの結合 "grunt-contrib-concat": "~0.5.0", // 簡易サーバー "grunt-contrib-connect": "~0.8.0", // Minify JS "grunt-contrib-uglify": "~0.5.1", // ファイルの更新検知 "grunt-contrib-watch": "~0.6.1", // CSSのプロパティをソートする "grunt-csscomb": "~3.0.0", // Minify CSS "grunt-csso": "~0.6.3", // シェルコマンドの実行 "grunt-exec": "~0.4.6" } }
– Gruntfile.coffee (タスク)
module.exports = (grunt) -> # load package.json grunt.initConfig pkg: grunt.file.readJSON 'package.json' compass: dist: options: config: 'config.rb' outputStyle: 'compressed' environment: 'production' dev: options: config: 'config.rb' coffee: compile: options: join: true files: 'source/js/script.js': ['source/coffee/**/*.coffee'] exec: mv_sprite: cmd: 'ruby mv_sprite.rb' autoprefixer: options: browsers: ['last 2 version', 'ie 8', 'ie 9'] default: src: 'assets/css/style.css' dest: 'assets/css/style.css' csso: default: src: 'assets/css/style.css' dest: 'assets/css/style.css' cmq: default: src: 'assets/css/style.css' dest: 'assets/css/style.css' csscomb: default: src: 'assets/css/style.css' dest: 'assets/css/style.css' concat: jsdefault: src: [ 'source/jslib/**/*.js' 'source/js/**/*.js' ] dest: 'assets/js/script.js' license: { src: [ 'source/jslib/_license.js' 'assets/js/script.js' ] dest: 'assets/js/script.js' } uglify: default: src: 'assets/js/script.js' dest: 'assets/js/script.js' connect: uses_defaults: {} watch: options: # enable livereload livereload: true compass: # watch scss files files: 'source/sass/**/*.scss' tasks: ['compass:dev'] coffee: # watch scss files files: 'source/coffee/**/*.coffee' tasks: ['coffee:compile'] js: # watch js files files: ['source/js/**/*.js', 'source/jslib/**/*.js'] tasks: ['concat:jsdefault'] image: # watch image files files: 'source/images/**' tasks: ['exec:mv_sprite'] html: # watch html files files: '**/*.html' # load Grunt Plugins grunt.loadNpmTasks('grunt-autoprefixer') grunt.loadNpmTasks('grunt-combine-media-queries') grunt.loadNpmTasks('grunt-contrib-coffee') grunt.loadNpmTasks('grunt-contrib-compass') grunt.loadNpmTasks('grunt-contrib-concat') grunt.loadNpmTasks('grunt-contrib-connect') grunt.loadNpmTasks('grunt-contrib-uglify') grunt.loadNpmTasks('grunt-contrib-watch') grunt.loadNpmTasks('grunt-csscomb') grunt.loadNpmTasks('grunt-csso') grunt.loadNpmTasks('grunt-exec') # tasks # defalt grunt.registerTask('default', ['connect', 'watch']); # development grunt.registerTask('dev', ['exec:mv_sprite', 'compass:dev', 'coffee:compile', 'concat:jsdefault']); # distribution grunt.registerTask('dist', ['exec:mv_sprite', 'compass:dist', 'autoprefixer', 'cmq', 'csscomb', 'csso', 'coffee:compile', 'concat:jsdefault', 'uglify', 'concat:license']); return
「grunt」の処理内容
・connect
簡易Webサーバーの立ち上げ (プロジェクトによっては使わない)
↓
・ファイルの更新の監視
1) images内が更新されたら…「3. スプライトイメージの自動化」で紹介したRubyコマンドを実行
2) scssが更新されたら…Compassを再コンパイル
3) coffeeが更新されたら…CoffeeScriptを再コンパイル → jsファイルが書き出される
4) jsが更新されたら…JSファイルを一つのファイルに結合
「grunt development」の処理内容
・上記 1),2),3),4)を順番に実行
「grunt distribution」の処理内容
・1)を実行
・2)をdist用の設定 (outputStyle: ‘compressed’) で実行
・ベンダープレフィックスを追加
・メディアクエリーをまとめる
・CSSのプロパティをソート
・CSSをMinify
・3)を実行
・4)を実行
・JSをMinify
・上部にライブラリ関係のライセンスを追加
ここまでの内容をまとめたものは、
https://github.com/soohei/grunt-template
にあります。
6. ローカルサーバー
本気で開発する場合は、MAMPでローカルサーバーを立てています。
詳しくは、
・MAMP 2 → MAMP 3
・MAMPでバーチャルホストの設定 (2種類)
あたりに書かれています。
7. その他
最後に、ここまでのワークフローで生まれた、
本番環境にアップ不要のファイルはサーバーに転送しないように、
Transmitを設定しています。