日々進化している最近のコーディング (フロントエンド開発) 環境について、
ここ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を設定しています。