gulpで階層構造を維持して出力する
以下のようなディレクトリ構造になるようgulpを用いてSassのコンパイルを行います。
devディレクトリがSassファイルを格納する開発用、destディレクトリがコンパイル後のCSSファイルを格納する納品用ディレクトリです。
root │ ├─ dev │ ├─ scss │ │ ├─ top │ │ │ └─ top.scss │ │ └─ products │ │ └─ products.scss │ │ │ └─ gulpfile.js │ └─ dest ├─ top │ └─ top.css └─ products └─ products.css
Sassのコンパイルをするタスクとして、以下のようなサンプルがオーソドックスな例としてよく紹介されています。
var gulp = require('gulp'); var sass = require('gulp-sass'); gulp.task('sass', function(){ gulp.src('./scss/**/**/*.scss') .pipe(sass()) .pipe(gulp.dest('../dest')); });
しかしこの場合、出力結果は以下のようになってしまいます。
root │ ├─ dev │ ├─ scss │ │ ├─ top │ │ │ └─ top.scss │ │ └─ products │ │ └─ products.scss │ │ │ └─ gulpfile.js │ └─ dest ├─ top.css └─ products.css
destメソッドで指定されたディレクトリ内に、並列で出力されてしまいました。 そもそもgulpでの出力は、複数のファイルを1枚のファイルにビルド・出力することを前提としており、そのためデフォルトでは指定されたディレクトリ内のファイルをすべて並列したものとして扱うよう設計されています。そのため上記のような結果になってしまったようです。
しかしgulpには柔軟なAPIがいくつか提供されており、その中に階層構造を維持したまま出力するオプションが用意されています。
第二引数として渡すオプションのうち、baseプロパティに対象とするディレクトリを指定することで、その階層構造を維持したままファイルをコピーすることが可能になります。
var gulp = require('gulp');
var sass = require('gulp-sass');
gulp.task('sass', function(){
gulp.src('./scss/**/**/*.scss', { base: './scss' })
.pipe(sass())
.pipe(gulp.dest('../dest'));
});
こうすることで、階層構造を保ったまま出力することができます。
root │ ├─ dev │ ├─ scss │ │ ├─ top │ │ │ └─ top.scss │ │ └─ products │ │ └─ products.scss │ │ │ └─ gulpfile.js │ └─ dest ├─ top │ └─ top.css └─ products └─ products.css
srcメソッドのオプションには他にもファイルを読み込まずにnullを返すreadプロパティや、巨大なファイルをストリームとして返すbufferプロパティなどがあるので、案件特性によって使ってみるのも良いでしょう。
出力した階層構造を制御する
しかし今後topやprodustsディレクトリにCSSだけでなくHTMLやJS、画像も格納することになった際、このままでは管理しにくくなってしまいます。
以下のように、出力先ディレクトリに新たにcssディレクトリを新設し、その中にファイルを格納するにはどうしたらよいでしょうか。
root │ ├─ dev │ ├─ scss │ │ ├─ top │ │ │ └─ top.scss │ │ └─ products │ │ └─ products.scss │ │ │ └─ gulpfile.js │ └─ dest ├─ top │ └─ css │ └─ top.scss └─ products └─ css └─ products.css
このようなケースには出力先のファイル名などをリネームするgulp-renameモジュールを利用します。
上記のUsageに記載の通り、このモジュールは非常に使い勝手がよく、ファイル名だけでなく拡張子やディレクトリ名まで変更することが可能です。 よく用いられる例としては圧縮したファイルに「min」を追加するようなケースがあります。 ちなみに以下のように、コールバック関数内にてファイル名を含むフルパスを取得できます。
rename(function (path) { console.log(path.dirname); // ファイル名を除いたディレクトリ名 console.log(path.basename); // 拡張子を除いたファイル名 console.log(path.extname); // 拡張子 })
このモジュールを次のようにパイプすることで、狙い通りのディレクトリに出力することが可能です。
var gulp = require('gulp'); var sass = require('gulp-sass'); var rename = require('gulp-rename'); gulp.task('sass', function(){ gulp.src('./scss/**/**/*.scss', { base: './scss' }) .pipe(sass()) .pipe(rename(function (path) { path.dirname += '/css'; })) .pipe(gulp.dest('../')); });
こうすることで、出力先にcssディレクトリがなければ作成し、あればその中にファイルが格納されます。
重複になってしまいますが、出力先は以下のようになります。
root │ ├─ dev │ ├─ scss │ │ ├─ top │ │ │ └─ top.scss │ │ └─ products │ │ └─ products.scss │ │ │ └─ gulpfile.js │ └─ dest ├─ top │ └─ css │ └─ top.scss └─ products └─ css └─ products.css
他にも様々なオプションやAPIが提供されているので、用途に合わせて使い分けることで、非常に柔軟な対応が可能になります。
Vagrantで文字化けする
Shift-JIS / EUC-JPの場合
Apacheにてデフォルトの文字コードにUTF-8を指定していることが原因なので、httpd.confにて下記をコメントアウト
AddDefaultCharset UTF-8
UTF-8なのに文字化けする場合
ロケールが原因
先ほどは日本語専用の文字コードの場合でしたが、Apacheがデフォルトの文字コードにしているUTF-8であるにも関わらず文字化けしてしまうことがあります。
正確に申しますと文字化けというよりかは、コメント中に和文が入っているとそれ以降をブラウザが正しく解釈できないようです。
Boxにもよりますが、多くの場合デフォルトのロケールは英語(US)になっているそうで、これによりファイルの文字コードがUTF-8だとしても正しく解釈できなくなってしまうそうです。
日本語のロケールがないことが原因のため、まず日本語パッケージをインストールしたのち、ロケールを追加します。
yum -y groupinstall "Japanese Support"
localedef -f UTF-8 -i ja_JP ja_JP.utf8
最後に、ロケールの設定ファイル(/etc/sysconfig/i18n)を編集して、システムのロケールを日本語に変更。
■変更前 LANG="en_US.UTF-8"
■変更後 LANG="ja_JP.UTF-8"
Vagrantでphpmyadminが403エラーになる
httpd.confのドキュメントルートディレクティブに以下を記述
AllowOverride All
インスペクタを用いたモバイル端末での検証方法まとめ
実機での挙動を実機で確認
モバイル用コンテンツの制作をする際、多くの場合はPC用ブラウザのエミュレータであったり、仮想環境にて挙動を確認します。
しかしごく稀に実機でしか起こり得ない崩れやバグが発生することがあります。
検証しなくてもある程度の目安をつけられるものならよいのですが、どうしてもインスペクタを起動して確認しなければ問題の原因を特定できないケースがあります。
以前はAdobe Edge Inspectというツールがありましたが、現在は入手することができません。
そこで2017年現在での検証方法をご紹介します。
Androidの場合
こちらの記事を参考にまとめました
- USBケーブルで端末とPCを接続し、Google Chromeを起動。
- アドレスバーにchrome://inspectを入力して開く。
この時、スマホのほうも確認したいサイトをブラウザで表示しておくこと。 - chromeに開きたいページのURLを入力し、Openボタンを押下
- 開くことに成功したら、いくつかボタンが現れるので、そのうちのInspectボタンを押下。
- chrome上にインスペクタが現れるので検証が可能になります。
iOSの場合
こちらの記事を参考にまとめました
Canvasのレスポンシブ対応
Canvasのレスポンシブ対応はなぜ難しいか
Canvasは複雑なグラフィックの描画から、マルチデバイスに対応したアニメーションの実装、3Dの描画まで幅広く対応できる優れものです。
ところがレスポンシブ対応となると格段に難易度が上がります。
その理由について整理してみると、
- 属性値でサイズを指定するため、CSSでのサイズ指定ができない【こちらを参照】
- 画像描画の場合、素材となる画像から切り出すサイズ・開始座標を絶対値で指定してCanvasに描画するという考え方である【こちらを参照】
主にこの2点により、レスポンシブデザインやRetina対応ではセオリーとなっている「width:100%」などといった、親要素を基準としたサイズ指定ができないことが難易度を高くしているゆえんとなっています。
ただし、いくつかのテクニックやメソッドを組み合わせることでこれらに対応することが可能となります。
ちなみにzoomやtransformをページ全体にかけるという方法も考えられますが、サイト特性によっては他の要素に縮小をかけられない場合もあるかと思いますので、ここではcanvasのみで実装が完結する方法をご紹介します。
レスポンシブ対応
先に、基本的な考え方からご紹介します。
- 素材となる画像と同じアスペクト比となる要素を準備し、その要素のサイズをCanvasの属性値として設定する
- 素材となる画像サイズと現在のCanvasサイズとの比率を算出し、その値にもとづいて画像をCanvas上に描画する。
1.2を画像ロード時に、2をリサイズ時に行うことでレスポンシブ対応を実現することができます。
素材と同じアスペクト比を持つ要素を準備する
基本的なロジックは以下の記事と同様です。
透過画像を用いてもよいのですがソース的に美しくないため、padding-topを用いた方法を採用します。
■HTML
<div id="canvas-container"> <canvas id="canvas"></canvas> </div>
■CSS
#canvas-container{ position: relative; height: 0; overflow: hidden; padding-top: 56.25%; } #canvas{ position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
div#canvas-containerはリサイズしてもアスペクト比16:9を維持し続けます。
canvasはスタイル上ではこのDIV要素と同サイズになるよう指定していますが、先述したとおりCSSでサイズを指定できないので、ここから先はJavascriptを用いて実装してゆきます。
親要素のサイズにもとづいてCanvasのサイズを指定する
下記ソースの通りです。ドキュメント上に描画された要素のサイズはclientWidth/clientHeightで取得することができるため、それをCanvasのDOMオブジェクトのプロパティに代入します。
var container = document.getElementById('canvas-container'); var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); //親要素のサイズをCanvasに指定 canvas.width = container.clientWidth; canvas.height = container.clientHeight;
素材となる画像を描画
画像の描画はdrawImageメソッドを用います。
drawImageメソッドは9個の引数によって画像の指定した範囲をCanvas上に描画することができるのですが、今回は特にクロップなどは行わないため、引数を3つのみ指定します。
第1引数は素材となる画像のオブジェクトを、第2・第3引数には描画範囲のx座標・y座標を指定します(画像を丸ごと描画するならば両者とも0を指定します)。
詳しくはリファレンスをご覧になったほうがより理解しやすいかと思いますので、併せてご覧ください。
先ほどのソースに追記してゆきます。
var container = document.getElementById('canvas-container'); var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); //イメージオブジェクトを生成 var img = new Image(); img.src = "/path/to/image/hogehoge.png"; //親要素のサイズをCanvasに指定 canvas.width = container.clientWidth; canvas.height = container.clientHeight; //画像読み込み後にdrawImageメソッドでCanvas上に描画する img.onload = function(){ ctx.drawImage(img, 0, 0); }
画面サイズに合わせて、Canvasを(内部的に)縮小させる
CSSでの「width:100%」に相当する描画ロジックを作ります。setTransformメソッドを用いることでCanvasを変形します。
setTransformメソッドについては後述しますので、まずは先ほどのソースに追記をします。
var container = document.getElementById('canvas-container'); var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); //イメージオブジェクトを生成 var img = new Image(); img.src = "/path/to/image/hogehoge.png"; //親要素のサイズをCanvasに指定 canvas.width = container.clientWidth; canvas.height = container.clientHeight; //画像読み込み後に、setTramsformメソッドで適切な比率に縮小 //その上で、drawImageメソッドでCanvas上に描画する img.onload = function(){ var scale = canvas.width / img.width; ctx.setTransform(scale, 0, 0, scale, 0, 0); ctx.drawImage(img, 0, 0); }
setTransformメソッドは、CSSでのtransform同様に、scale/skew/translateのすべてを行うことができます。
6つの引数をとり、それぞれの引数に値を指定することで、これらの変形を同時に行うことができます。
6つの引数の意味については、以下の通りリファレンスより引用します。
setTransform(a, b, c, d, e, f)メソッドのそれぞれの引数は、 setTransform(伸縮x, 傾斜y, 傾斜x, 伸縮y, 移動x, 移動y)となります。 何も変形しない場合の引数の値は、setTransform(1, 0, 0, 1, 0, 0)となります。
今回のケースでは、単に伸縮のみを行うため、算出した伸縮率を第1引数・第4引数に指定しています。
リサイズ時
ここまでの一連の流れを、リサイズ時に再描画することで、レスポンシブ対応が可能になります。
先ほどのソースに追記します。
var container = document.getElementById('canvas-container'); var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); //イメージオブジェクトを生成 var img = new Image(); img.src = "/path/to/image/hogehoge.png"; //親要素のサイズをCanvasに指定 canvas.width = container.clientWidth; canvas.height = container.clientHeight; //画像読み込み後に、setTramsformメソッドで適切な比率に縮小 //その上で、drawImageメソッドでCanvas上に描画する img.onload = function(){ var scale = canvas.width / img.width; ctx.setTransform(scale, 0, 0, scale, 0, 0); ctx.drawImage(img, 0, 0); } //リサイズ時 window.onresize = function(){ var scale = 0; //再描画のため必ずCanvasの描画領域をクリアする ctx.clearRect(0, 0, canvas.width, canvas.height); canvas.width = container.clientWidth; canvas.height = container.clientHeight; scale = canvas.width / img.width; ctx.setTransform(scale, 0, 0, scale, 0, 0); ctx.drawImage(img, 0, 0); }
こちらをまとめます。
var container = document.getElementById('canvas-container'); var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d');
var isInit = false; var img = new Image(); img.src = "/path/to/image/hogehoge.png";
//描画 var render = function(){ var scale = 0;
if(isInit){ ctx.clearRect(0, 0, canvas.width, canvas.height);
}else{
isInit = true;
} canvas.width = container.clientWidth; canvas.height = container.clientHeight; scale = canvas.width / img.width; ctx.setTransform(scale, 0, 0, scale, 0, 0); ctx.drawImage(img, 0, 0); } //メイン var main = function(){ img.addEventListener('load', render, false); window.addEventListener('resize', render, false); } //メイン実行 main();
こうすることで、Canvasでもレスポンシブ対応をすることができます。
以下がそのサンプルです(画面をリサイズして、画像が拡縮されることを確認してください)。
See the Pen Canvas Demo by Shin Imae (@shinimae) on CodePen.