2015年5月13日水曜日

脱jQueryするにはdocument.querySelector()もあるけど状況どう?

AngularJS内蔵のjqLiteを使うことにしたが、DOMElementを取得するのにdocument.getElementById()というのは使いにくい場合もある。

ところで、jQueryライクなものとしてdocument.querySelector()もあるけど、状況はどうなのか?と。

Can I use... Support tables for HTML5, CSS3, etc

な〜んだ。全然大丈夫じゃん!なら、もはやjQueryも必須ではないし、getElementById()も要らないじゃん!と思ったが…

document.querySelector - Web API インターフェイス | MDN

【訳注: 上記ではクラスセレクタを用いた検索を例示していますが、この様な単発のクラス名の場合は getElementsByClassName() メソッドを用いた方が高速な動作となります。また、ID セレクタ ( # ) を対象とする検索も可能ですが、その場合は getElementById() メソッドを用いた方が高速です。対象セレクタが流動的で有る場合や、または隣接セレクタなどによるコンビネーションセレクタでの複雑な検索の場合に於いて、querySelector() や querySelectorAll() は真価を発揮します。】

ということなので、要らないということは無いようだ。

つまり、複雑なクエリ条件の場合はquerySelector()、単純な場合はgetElementById()とかgetElementsByClassName()と使い分けをした方が良いと。

angular.element()のjQueryを読み込んだ場合とそうでない場合(jqLite)の動作の違い

できるだけページを軽量化するためにjQueryを読み込まないようにしようと思った。しかし、jQueryは便利なので捨てがたいなと思っていた。そんな時、angular.element()というメソッドがあることに気がついた。

AngularJS: API: angular.element

AngularJSは内部でjqLiteというjQueryのサブセット版を持っていて、angular.element()はそれを使ってjQueryのオブジェクトを返してくれるらしい。

ただ実際に使ってみると、jQueryを読み込んだ場合とそうでない場合で動作が違うのでハマった。

jQueryを読み込んでいる場合はこのように#targetを取得できるが、そうでない場合、つまりjqLiteのみの場合は取得できない。

var $elm = angular.element('#target');

要するにjQueryを読み込んでいる場合は次と等価だ。

var $elm = $('#target');

一方、jqLiteのみの場合はこのようにする。

var elm = document.getElementById('target');
var $elm = angular.element(elm);

Wraps a raw DOM element or HTML string as a jQuery element.

というのはそういう意味だったのか!とやっと分かった(^^);

2015年5月5日火曜日

ionicプロジェクトのsplash.pngを自動でリサイズしながら9-patchする「make-9patched-splash-ionic」

先日、nodejsで9-patch画像を自動生成する方法について書いた。

琴線探査: nodejsで9-patch画像を自動生成する「FourSide1px9patcher」

その後、このコードを使いやすいようにnodeモジュール化した。

junkoro/four-sides-1px-9patcher

ここからが本番。ionicプロジェクトでsplash.pngを自動的にリサイズしつつ9-patchするコードを書くときがキタ(・∀・)!!

すでにnodeモジュール化して公開済み。

junkoro/make-9patched-splash-ionic

インストール方法やコード内容についてはgithubに譲るとして、ここでは開発時に困ったことについて書いておきたいと思う。

9-patchについては開発済みだったので何の問題も無かったが、画像のリサイズが困った。node-canvasでもリサイズはできるのだが、リサイズ後の画像のクオリティーがとても使えたものではないのだ(´・ω・`)

この問題はブラウザ上のJSでも同じで、以前に研究したことがあった。

琴線探査: HTML5 Canvasで画像のリサイズするならコレ!「JS-Image-Resizer」

というわけで、再びこの「JS-Image-Resizer」を使わせていただくことにした。結果は上々だ。

ただ、「JS-Image-Resizer」はブラウザ用のJSコードなので、node用に多少変更する必要があった。と言っても、「modules.exports」行の追加とエラーになる部分(WebWorker部)をコメントするくらいだったが。

これでこの件にコメントすることができるようになった。

Bad splashscreen ratio with Android using CLI new feature - ionic - Ionic

この件は自分も困っていたが、他にも多くの人が困っているようなので、このコードが助けになればと思う。

2015年5月3日日曜日

nodejsで9-patch画像を自動生成する「FourSide1px9patcher」



追記15.05.05:使いやすいようにnodeモジュール化した。
junkoro/four-sides-1px-9patcher

9-patchツールの現状 〜 開発意図

前から9-patchを作るのは面倒だし問題だと思っていた。そこで改めて現状を調べてみたが、やはり9-patchを作るには

・Android SDKのdraw9patch
・Android Asset Studio - Simple Nine-patch Generator
・Eclipseプラグイン「Draw 9-patch Tool
・Fireworksなど汎用の画像編集ソフトを使う

という感じ。nodejsで、特にgulpで9-patchを自動化できるツールが無いか探したが、これくらい。

gulp-9-patch

このツールはスケーリングしたり非9-patch化する機能しかなく、画像を9-patch仕様に加工することはできない。


開発要件

やはり独自開発するしかないか…もう我慢ならん!こんな ('A`)マンドクセ-ことイチイチやってられるかっての!

ただ、なぜこのような状況なのかは理解できる。9-patchは柔軟性が高く、画像によってパッチすべき場所は変わる。これが自動化ツールが存在しない理由なのだろう。

しかし、画像を作る側が9-patchしやすい条件を作ればどうだろう?たとえばこんな画像で、単純に4辺の周辺1ピクセルを画面にあわせて伸ばすように9-patchすることを考える。

画像はionicのデフォルトスプラッシュスクリーン用の画像

この条件を満たすケースは意外と多いのではないかと。自分的には自動化するためにデザインを犠牲にする覚悟である(^^);

このようなことをしてくれるCLIツールを、後々ionicなどgulpやgruntをビルドシステムとして使っているようなものに組み込むことを考えて、nodejsで開発する。


nodejsでの画像処理をどうする? 〜 技術調査

調べると、みんなよくimagemagickとかを使ってやっているようだが、できればコマンドラインツールに依存するような形にしたくない。

というわけで、前から名前だけは知っていたけれど、よく調べたことがなかったこれを使ってみよう。

EaselJS | A JavaScript library that makes working with the HTML5 Canvas element easy.

EaselJSはHTMLのCanvasをラップしてFlashライクに扱えるようにするライブラリだ。

CreateJSというFlashコンテンツをHTML5に変換するのをサポートするランタイムライブラリのひとつ。当然Adobeが関係してる。node版もある。

CreateJS/EaselJS-NodeJS

これで描画関連は問題無いだろう。画像ファイル化はどうか?

Automattic/node-canvas

node-canvasはtoBuffer()というメソッドを持っていて余裕でPNG化できるらしい。このBufferをfsモジュールでファイルに書き出せば終了だろう。楽ぅ〜(*^_^*)

しかもPDF化とかSVG化もできるらしい。なんかいつの間にか色々やりたい放題な状況になっていたんだね\(^o^)/

そもそもEaselJS-NodeJSがこのnode-canvasに依存しているので、かなり成熟しているライブラリなのだろうと思う。とりあえずこれで。


開発準備

$ mkdir FourSide1px9patcher
$ cd FourSide1px9patcher

node-canvasをインストールするには、結構ネイティブ側の準備が必要らしい。さすがにネイティブと無関係とはいかないか(^^);

Installation OSX · Automattic/node-canvas Wiki

HomebrewとかMacPortとかでインストールした方がいいだろう。自分はbrew派なのでbrewで。

MacOSX - パッケージ管理システム Homebrew - Qiita

$ brew install pkg-config
$ brew install pixman
$ brew install cairo
$ PKG_CONFIG_PATH=/opt/X11/lib/pkgconfig npm install canvas


開発

後はエディターでコーディング。

コーディング中に気づいたのだけど、EaselJSは必要無かった(^^); 描画の部分はCanvasだけで事足りてしまった。

やっていることは、元画像に透明の枠をつけ、左上、左下、右上をこのように加工して9-patchする作業。

左上

左下

右上

//モジュール読み込み
var fs = require('fs');
var Canvas = require('canvas');
var Image = Canvas.Image;


/**
 * ピクセル描画
 */
function drawPixel(context, x, y, color) {
  context.save();
  context.translate(x, y);
  context.fillStyle = color;
  context.fillRect(0, 0, 1, 1);
  context.restore();
}


/**
 * 4辺の周辺1ピクセルを9-patch
 */
function fourSide1px9patcher(src, dst) {

  //イメージ読み込み
  var img = new Image;
  img.src = src;

  //9-patchは元画像の周りに1pxの透明の枠を付ける
  var cv = new Canvas(img.width + 2, img.height + 2);

  //コンテキスト取得
  var ctx = cv.getContext('2d');

  //イメージを座標(1, 1)に描画
  ctx.drawImage(img, 1, 1);

  //9-patchの線は黒
  var color = 'rgba(0, 0, 0, 1.0)';

  //ドット描画 - 左上-TOP
  drawPixel(ctx, 1, 0, color);

  //ドット描画 - 左上-LEFT
  drawPixel(ctx, 0, 1, color);

  //ドット描画 - 左下-LEFT
  drawPixel(ctx, 0, cv.height - 2, color);

  //ドット描画 - 右上-TOP
  drawPixel(ctx, cv.width - 2, 0, color);

  //ファイル書き出し
  fs.writeFile(dst, cv.toBuffer());

}


//実行
fourSide1px9patcher('src.png', 'dst.9.png');


実行

$ node FourSide1px9patcher.js

生成されたdst.9.pngをdraw9patchで開くと、意図通りに9-patchされていることを確認できた。

右側で中央のionicロゴが歪んでいない

draw9patchは画像が正しく9-patch加工されていても、拡張子が「.9.png」になっていないと9-patch画像として認識しないので注意する。

次はionicプロジェクトのリソース構造に合わせて9-patchするラッパーを書こう。

追記15.05.05:書いた。
琴線探査: ionicプロジェクトのsplash.pngを自動でリサイズしながら9-patchする「make-9patched-splash-ionic」