JavaScript デバッグ環境の用意(JSDT + Firebug + Crossfire)

前回に引き続きまして JavaScript の開発環境構築です。 今回は、少し大きなプログラムを作り始めるとなくてはならないデバッガを設定してみます。

JavaScript のデバッグ環境は、IE の開発者ツールや Firefox の FIrebug などでも準備できますが、ここでは Eclipse WTP/JSDT の環境のビューから直接ブレイクやウォッチをする方法をかいてみます。

WTP/JSDT についてはこちらです。

JavaScript 開発環境の用意(Eclipse WTP/JSDT) | hiromasa.another :o)

Eclipse に含まれる Web Tool Platform(WTP)には、JavaScript Development Tools(JSDT)が含まれており、おそらくみなさんがインストールしているであろう、Eclipse for  JavaEE Developer に最初から含まれています。

JSDT からブラウザの JavaScript にリモートアタッチしてデバッグする方法はいくつかあるようですが、JSDT の標準インストールで FIrebug / Crossfire プロトコルに接続して行うパッケージが入っていますので、こちらを用いてみました。

JSDT/Debug/Crossfire – Eclipsepedia

Support for remote Firebug using the Crossfire protocol is available in the JSDT development bundles and is provided to allow remote debugging of JavaScript using Firebug via Crossfire.

まず、Firebug / Crossfire を使うために Firefox に Firebug と Crossfire アドインをインストールします。 Crossfire の .xpi についてはこちらです。(下の方に .xpi へのリンクあり)

mrennie/crossfire · GitHub

Crossfire is a Firebug extension which implements a JSON protocol to allow remote clients (like an IDE or code editor) to connect to Firebug.

Firefox にアドインがインストールできたら、Eclipse から接続する準備を行います。Crossfire アドインのスタートがちょっとわかりにくいのですが、FIrefox の表示 -> ツールバー -> アドオンバーを表示して、右下のアイコンからスタートします。

130322-0001

アタッチポートを聞かれてくるので、ここではそのまま 5000 で起動します。

130322-0002

でもって、FIrefox でデバッグしたいサイトを開いて Firebug で一度「スクリプト読み込み」しておきます(これが重要)。ここでは Eclipse の jstest プロジェクトに配置された、http://127.0.0.1/jstest/index.html を開きました)

130322-0003

次に Eclipse 側のデバッグ構成をします。 デバッグ構成からリモート JavaScript に jstest 構成を追加します。 コネクターは Crossfire – Remote “Attach” を選択し、ソースからソースコードをマッピングします。

130322-0004

130322-0005

このステップで準備完了です。 そのまま右下デバッグボタンを押下すると、Firebug に接続され、先のアイコンが接続状態になり、Eclipse のデバッグプロセスも起動します。

130322-0007

何かデバッガの挙動がおかしいなと思ったらここから再起動してあげてください。なおります。(ブラウザとの通信状態によってブレイクポイントの位置などが不整合することがあるようです)

130322-0006

あとは通常の Eclipse のデバッグ操作です。 ソースコードにブレイクポイントをはってみます。 F6, F7, F8 キーなどでステップ実行やウォッチが可能です。

130322-0008

ブレイク中のウォッチはカーソルあてればホバーしてきます。

ふむ。

130322-0009

ほう。

130322-0010

ほほう。

130322-0011

変数ビューを使えば、Firebug から送られてくる生のブラウザの状態もみれます。

130322-0012

これを見ているだけでもブラウザの内部動作の理解ができますね。

この手順で Eclipse から Firebug にいったん接続すれば、あとはそっとしておいて大丈夫です。作業開始時にでもつないでおくといいでしょう。

機能的には Firebug でも当然同じことが出来ますが、Eclipse からであればそのまま見やすいソース表示でデバッグブレイクでき、変数ウォッチなどもビューを使って操作しやすいかなと思いました。 🙂

プログラムのなぞの挙動は、考えるよりデバッガひっかけたほうが解決簡単。論より run が座右の銘のひろましゃでありました。がってんがってん。(←調子が悪いとオチが弱くなるらしい・・・

JavaScript 開発環境の用意(Eclipse WTP/JSDT)

いわゆる業務系アプリをつくっている現場でも徐々にモダンな JavaScript の開発が導入されるようになってきました。もちろん今までもバリデーションやコントロールの制御に使われてきましたが、なんというかノリっていうか、Java プログラマーがその知識だけで、それとなく動くものをつくってきたように思います。

そのような昔ながらの JavaScript しかみていないと、jQuery などで使われている近代的な JavaScript 記述方法はもはや別の言語にも見えてしまいます(笑)

てなかんじで、そろそろちゃんと JavaScript を勉強しないといけないねって感じも含めて去年末にプログラムをかいたのが「バッタになりたい魔法使い」でした。

Web絵本|バッタになりたい魔法使い

wizard_ogp

内容の詳細はこちらを。:)

canvas と Web 仕掛け絵本 | hiromasa.another :o)

実際の実装ですが、絵本のコマに対して canvas タグを対応させ、CSS では canvas を display: block; してブラウザの描画領域を全て埋め、横幅、余白を含め全ての描画制御を JavaScript から行っています。たとえば、背景画像横幅が足りなければ CSS で真ん中にもっていくのではなく、”白”を画像の左右に描画しているイメージです。

さて、JavaScript は特に Java のような静的型付け言語しか知らない場合、非常に特異な言語に見えるかもしれません。プロトタイプベースのオブジェクト指向や、コンテキストの考え方など、一般的なプログラミング言語の常識が通用しない部分が多々あります。

またブラウザという実行環境についても知識が必要で、HTML/CSS の操作や、そもそもどこから言語の機能で、どこからブラウザの実装なのか分からなかったり、なかなか難儀なものです。

専門の人が現場にいればいいのですが、お客さんによってはそもそもこういったジャンルの仕事があると思っていない場合もあり、小技を持っている専門外プログラマーの英知をもって、ひどいものができるという惨状を目の当たりにし、ぼくはついに立ち上がった、、かどうかは分かりませんが、よくある Java の現場で JavaScript をかかねばいけなくなった時の環境の用意の仕方や、ちょっとしたテクニックを書いてみたいと思います。 🙂

良い仕事には良い環境から。

よくある Java の現場ってことで Eclipse を用いていますが、「バッタになりたい〜」でも Eclipse 使っています。この方法は普通に JS かく場合にも便利ですのでお試しを。

Eclipse に含まれる Web Tool Platform(WTP)には、JavaScript Development Tools(JSDT)が含まれており、おそらくみなさんがインストールしているであろう、Eclipse for  JavaEE Developer に最初から含まれています。

Eclipse から JS サポートをうけるには、使っているプロジェクトを「JavaScript プロジェクトに変換」してあげます。変換といっても、機能追加みたいなイメージなので、その他は現状通りです。

ここでは新規にプロジェクトをつくって変換しています。「JavaScript プロジェクト」を新規に作成することも出来るのですが、一般的によくやるワークスペース外の htdocs の下にプロジェクトを配置する操作を行うと、JavaScript リソースの設定がおかしくなるようなので、いったん別のプロジェクトから変換するというやり方をとっています。

130320-0001

jstest をプロジェクト名としました。

130320-0002

サンプル的にファイルを配置してみました。ここでは js フォルダの下にソースを置くとします。

130320-0003

プロジェクトを右クリックして、「構成 -> JavaScript プロジェクトへ変換」します。既存プロジェクトがある場合はここからの操作です。

130320-0004

この後、ソースファイルの配置などを決めるダイアログがでますが、そのままOKしてください。(どうもこの段階で設定などをすると JavaScript リソースがおかしくなるようです)

これでプロジェクトが次のようになると思います。

130320-0008

JavaScript リソースが追加され「ECMAScript ビルトイン・ライブラリー」と「ECMA 3 ブラウザー・サポート」が入れば成功です。補完などが効かなくなったら、この二つが正しく入っているかを確認します。(ここで書いている手順以外だと、ビルトインライブラリーが入らないパターンがあるようです。。)

このままだとJavaScript リソースがプロジェクト配下の全てのファイルを対象としてしまうので(特に問題はないですが)、ソースファイルの位置をプロジェクトの右クリック -> プロパティー -> JavaScript -> ソース -> フォルダの追加から指定します。(jstest/js にしました)

130320-0005

さて、これで準備完了です。ようこそ、JavaScript の世界へ。 🙂

統合環境はプログラム言語の勉強のよき先生です。特に、静的型付け言語の場合は、コンテンツアシストやインテリセンスといった補完機能やリアルタイムエラー出力などの IDE の強力な機能が、いろいろな手助けをしてくれます。

オブジェクト指向なんか統合環境ぽちぽちいじって、コード補完駆使してフレームワークのライブラリ呼んでるうちに覚えるもんじゃなかろうか、なんて思ったりしますがどうでしょうか。ぼくはそうでした(笑)

JavaScript の場合動的型付け言語なので、IDE は少し貧弱な先生になりさがってしまいますが、それでもあるとないのとではだいぶ違います。

まずは、プロジェクトに追加された JavaScript リソースを眺めるところから。これはコンテンツアシスト用の JSDoc になっています。

ECMAScript ビルトインライブラリー。これが JavaScript の言語としての実態になります。これで全部ですがほとんどなんもありません。

130320-0009

ECMA 3 ブラウザサポート。見たような名前が並んできましたが、これは全部表示しきれていません。まだまだあります。これらはブラウザが言語につっこんできているオブジェクトとなります。

130320-0010

この中で、Window オブジェクトを選択して JSDoc をみると次のような記載があるのが分かります。

/**
 * Object Window()
 * @super Global
 * @constructor
 * @since Common Usage, no standard
*/
function Window(){};
Window.prototype = new Global();
Window.prototype.self = new Window();
Window.prototype.window = new Window();
..略..
Window.prototype.document= new HTMLDocument();

Global は JavaScript 側のオブジェクトですが、これをプロトタイプにして Window オブジェクトが生成されています。こんな感じで ECMAScript とブラウザは、グローバルを介してつながっている感じです。ちなみに「@since Common Usage, no standard」の記述がもの悲しい・・・。

こんなのを眺めていれば、alert() や document.getElementById() なんかがなぜその記述で動くのかが分かるようになりますね。

ちなみに Eclipse がこれらの JavaScript リソースを使ってコード補完ができるのは、次の設定によります。「プロジェクト -> プロパティ -> JavaScript -> インクルードパス -> グローバルスーパータイプ」

なんだかちょっとバグっているのですが「フィールドおよびメソッドを継承する元の SuperType」のところがブラウザと同様「Window()」になっているのが分かります。”null 中の”ってなんだって感じですが(笑)

130320-0014

というわけで、コード補完をやってみます。

グローバルスコープ。

130320-0027

window。

130320-0011

String オブジェクト1。

130320-0012

String オブジェクト2。static のように。

130320-0013

Math。あれ。。。(バグ?)

130320-0022

という感じで、JSDoc みたり、補完に出てくる候補を見たり実際にブラウザでグローバル this の中身を表示していたりすると、JavaScript についての理解が進むのではないかと思います。

なおあんまり関係ありませんが、JSDT jQuery を導入すると jQuery の補完もできるようになります。

130320-0015

130320-0016

次の変数名の補完はなんかちょっと微妙です。this がからむと厳しいですね。 JSDoc の @memberOf は効くようなのですが、、@this タグが使えるとなんとかなるのかもしれませんが、この辺は動的型付けの難しさ。

ほほう!

130320-0019

無念。。

130320-0021

最後に、リファクタリングを。 あらかじめお断りしておきますと、ローカル変数名変更以外のリファクタリングはうまく動かない感じです。でも、これだけでもだいぶ違います。

適当すぎるローカル変数名リファクタ用サンプルコード。

130320-0023

とりゃ。 hoge -> moge 変更。

130320-0024

パチパチ。

130320-0025

その他、リアルタイムエラー申告も役立つことでしょう。

未使用変数申告と、セミコロン忘れ。どっちもやりがちで、プログラムは動いちゃうパターンです(8行目、10行目)。こういうのは IDE にお任せです。だいぶ助けられます。

130320-0018

というわけで、いろいろ見始めると深みにはまっていく JavaScript の世界。ついブラウザのソースコードまでダウンロードし始めてしまいましたが、、そろそろやめておきましょう。。

とりあえず、現場で JavaScript かかなければいけなかったりしたときに思い出していただければ!

canvas と Web 仕掛け絵本

明けましておめでとうございます。 🙂

昨年のご愛顧も込めまして、「WordPress デザインワークブック」共著のコモモさんこと高橋朋代さんと Web 仕掛け絵本のサイトをつくりました。とてもかわいい感じになりましたので、良ければ見てください!

バッタになりたい魔法使い – pararaehon.com

PC/Mac はモダンブラウザ(IE8 以下はごめんなさいです)に対応しています。

モバイル系は iOS は 6 以降の Mobile Safari、Android は 4 以降の Chrome が良い感じで見れます。Android 2 の方は Firefox 17 を使うとゆっくりですが動作します。3G 回線の場合は、ちょっと読み込みに時間かかりますので、白くなったりしたらリロードしてみてください。キャッシュするときれいに動きます。

iPhone/iPad は 4S 以降(Apple A5)でフルスペックで動くはずです。すごい性能。iPhone の場合は Retina ででます。絵本的になりますので iPad3 もおすすめです。ベンチマークにもどうぞ。

自分の本職はバックエンド側の処理、業務系アプリの処理やインフラなどなのですが、WordPress を含めこういった Web サイトをつくる場合には、芸術性のあるデザインやブラウザフロントエンド処理が不可欠です。

WordPress デザインワークブック” の執筆ではこの部分にコモモさんの全面支援をもらっていますが、”バッタになりたい魔法使い”では絵をコモモさんに、ひろましゃはお話と、なんと初 JavaScript をやっております。 動きも面白いですので良ければその辺もご覧ください。 🙂

さて、去年はこのブログで「WordPress 徹底解析」シリーズを週1でやろうと始めておりましたが、少し忙しくなってしまい止まってしまってごめんなさい。2月くらいからまた時間が出来ると思いますので再開したいと思います。

今年もどうぞよろしくお願いいたします!


WordPress ではないですが、せっかく Web 系のネタですのでこの仕掛け絵本の仕組みを解説したいと思います。 まずは動作見て頂ければ。スクロールを上下するとぴょこぴょこ動くのが分かると思います。 🙂

バッタになりたい魔法使い

Web 仕掛け絵本で実現したかったことは2つでした。

  1. 60FPS のフルフレームで動作させる
  2. スマートフォン・タブレットでも動かす

絵本という前提があるので、コンピュータ的なフレーム落ちしたガタガタした動作は物語の世界を壊すと考え、ブラウザに 1/60 秒フレーム描画(canvas & requestAnimationFrame)をさせています。また、スマートフォン、タブレットでみれたら絵本っぽいなというのも考えました。

このため、canvas と requestAnimationFrame(もしくはベンダープレフィックス)が使えないブラウザでは動作しません。IE で言えば IE9 以降に対応します。ちなみに IE9 は結構速かったです。スマートフォン系では iOS6 以降、Android 4(標準ブラウザもしくは Chrome、Firefox17)が動作します。iOS5 の方、ごめんなさい…

この仕組みにパララ絵本という名称をつけていて、いわゆるパララックス効果も入っているのですが、通常のパララックスとは異なります。というのも、普通のパララックスはブラウザの縦座標(scrollTop)に対して、描画オブジェクトの位置を決めますが、パララ絵本ではスクロール位置を動かすタイミングとしてだけ使って、あとはタイマー処理しています。これは 60 フレーム描画をするためと、スマートフォン(Mobile Safari)がスクロール中動作にイベントが発生しないことに対する仕様です。

実際の実装ですが、絵本のコマに対して canvas タグを対応させ、CSS では canvas を display: block; してブラウザの描画領域を全て埋め、横幅、余白を含め全ての描画制御を JavaScript から行っています。たとえば、背景画像横幅が足りなければ CSS で真ん中にもっていくのではなく、”白”を画像の左右に描画しているイメージです。

ブラウザにやらせるよりも自前で制御したほうが、必ず見せたい領域を中央にもってくる処理や、 devicePixelRate(retina)の対応など、描いてもらった絵を忠実に再現させるのが楽だったというのが理由です。(このためちょっとブラウザリサイズの処理は遅いです)

<body id="parara">
<canvas></canvas>
<canvas></canvas>
...
</body>
body {
    margin: 0;
    padding: 0;
}

canvas {
    display: block;
}

JavaScript のソースはオーソドックスな、いわゆるゲーム描きです。requestAnimationFrame(render) と通常タイマー(update)の二つをつかった 1/60 処理です。

/**
 * Event initialize.
 */
Scene.prototype.start = function() {
    this.hack();
    window.addEventListener('load', this.init.bind(this), false);
    window.addEventListener('resize', this.init.bind(this), false);
    window.addEventListener('orientationchange', this.init.bind(this), false);
    window.addEventListener('scroll', this.update.bind(this), false);
    window.setInterval(this.update.bind(this), 1000 / ANIMATION_FPS);
    this.render();
};

/**
 * render.
 */
Scene.prototype.render = function() {
    requestAnimFrame(this.render.bind(this));
    // viewport range render.
    ...
    ...

...
/**
 * requestAnimationFrame wrapper.
 * context invalid?: Illegal operation on WrappedNative prototype object
 */
window.requestAnimFrame = (function() {
    return window.requestAnimationFrame
    || window.webkitRequestAnimationFrame
    || window.mozRequestAnimationFrame
    || window.oRequestAnimationFrame
    || window.msRequestAnimationFrame
    || function(/* function */callback, /* DOMElement */element) {
        window.setTimeout(callback, 1000 / ANIMATION_FPS);
    };
})();

オブジェクトの設計は、複数あるコマ(Scene)、コマ(Page)、コマ内の描画オブジェクト(Actor)と分割し、Scene ではブラウザのリサイズやスクロール位置処理と update render タイミングをとる処理、Page では Actor の実座標や動作タイマー(tick)制御、Actor は描画処理(canvas.drawImage)という感じになっています。

描画オブジェクトも全てビットマップ(Image)ですので、これらに対する DOM の操作はありません。全て canvas コンテキストに描いています。

Actor の座標はそれぞれに設定された動作開始スクロール位置からタイマーで +1 増加を始める tick 値によって決まり、逆スクロールでは時間(tick)を巻き戻すことで元の配置に戻るようにしています。つまり x += 1; とやるわけではなく x = tick とするイメージです。

隠しコマンドで「d」キーを押すとデバッグ表示になるようにしています。たぶん見るとすぐ分かると思います。

また、Actor の座標に関しては PararaEhon.js 初期化時にコンストラクタで定義できるようになっており、update や efect(drawImage する直前)をメソッドを処理時に差し込み、仕掛け絵本ライブラリとして使えるようにしています。

/**
 * バッタになりたい魔法使い.
 *
 * @author komomo and hiromasa
 * @version 1.0
 */
new PararaEhon.Scene({
    id : 'parara',
    width: 960,
    height: 600,
    book : [
        /**
         * ページ01
         */
        {
             bg: {
                 image: 'images/page01/01_bg.png',
                 fixed: true
             },
             wizard: {
                 image: 'images/page01/01_m.png',
                 x: 50,
                 y: 25
             },
             .....
        },
        /**
         * ページ02
         */
        {
             .....
             hitsuji01: {
                 image: 'images/page02/02_hitsuji01.png',
                 x: 15,
                 //y: 320,
                 start: 200,
                 update: function() {
                     this.y = 600 + (1 - Math.exp(-6 * (this.tick / 60))) * -280;
                     if(this.tick > 60) this.complete = true;
                 }
             },
             .....
/**
 * Constructor.
 */
Actor = function(context, object) {
    .......
    this.update = this.update ? object.update.bind(this) : null;
    this.effect = this.effect ? object.effect.bind(this) : null;
    .......
};

さて、キモとなる canvas.drawImage ですがブラウザによっていろいろ面白い動作がありました。Gecko や Webkit のソースまで見ていないので本当に見た目だけの判断です。間違っている可能性大きいです。

  1. Firefox17 と IE9 は スクロールで見えなくなっている canvas にもまじめに drawImage している?(見えているコマだけ render したら速くなりました)
  2. Firefox17 の Mac と Windows 版は描画が少し遅い。Linux 版(しかも性能の劣る機械)だとフルルフレームで描画される。(本当にこれはなんとなくなのですが Linux 以外は Image が GPU じゃなくてメインメモリから転送されているような。うまく VRAM に乗らなかった?気のせい?)
  3. Firefox では drawImage の引数で画像範囲外を誤って転送させても動くが、他のブラウザだと動作しない。(動かない動作は OpenGL で同様の間違いをしたときの動作に似ている)
  4. Mobile Safari に過激に複数の canvas をレンダリングさせると落ちる時がある
  5. Android Firefox 17 が結構がんばって canvas 描ける。(遅いですが)
  6. 速度の印象は Safari < Chrome < Firefox17 = IE9 という感じでした。がんばれ Firefox!(もうすぐでるであろう Firefox18 / IonMonkey が楽しみです)

なにぶんにもモダンな JavaScript をかくこともブラウザもほとんど初めてで、押し迫る期限の中座標処理でうひょ〜となってしまった部分もあり、まだまだ不出来ですがもし興味ありましたらソースを見てみてください。 🙂

PararaEhon.js

maho.js

というわけで。

みんなバッタになっちゃえ!