AJAXでコジャレたホームページ作り、その2

 わずか営業日二日という、私としては例外的に短い日本出張の後は、シアトルには帰らずポートランドに直行し、Railsカンファレンスに参加だ。

 時差ぼけの頭でObjective-Cのプログラミングは辛いので、眠気覚ましに「Javascriptでコジャレたホームページ作り」に再挑戦。

 先日のものより改良したのは以下の三つ。

1) サムネールを個別の画像として取得せずに、一つの画像として取り込んで切り刻んで表示
2) ダミーのイメージファイルを使って、テキストのアンダーラインとアニメーション領域の下部を一致させる
3) ianime.jsのベジエ・アニメーション機能を最大限に使って、よりオシャレにする。

 出張中で仮想Windowsを積んでいないMacBookしかないので、Mac版のSafari/Firefox/Operaでしかテストをしていないが、それなりに動いている。それに加えて、1)のおかげでiPhone上のSafariでもちゃんと動くようになったのがうれしい。デモは以下のリンクをクリックすると別ウィンドウで始まる。

 Logo

 ブラウザーのコンパチリビティには気をつけて書いたつもりなので、大抵のブラウザーでは動くはず。ただし、1)によりIE6のキャッシュのバグが回避できたかどうかは未確認。予想される一番の問題点は、あまりにたくさんの画像を同時に動かしているために、遅いマシンではフレームレートが極端におちてしまうこと(iPhoneだと3〜5frame/secになる)。比較的遅めなマシンでのアニメーションのスムーズさなどに関する感想をいただけるとありがたい。

 ちなみに、2)の解決方法のもとになったのは、コメント欄でいただいた「staticを使えば?」というもの(感謝、感謝)。最終的にはstaticは使わなかったのだが、absoluteでアニメーションエリアにオーバーラップさせたDIVの中で、アニメーションエリアと同じ高さの画像(ただし、visibility:hiddenなので非表示)とテキストを並べることにより、ベースラインの位置を任意にコントロールするというもの(例えば、ページの上から120pxの所にベースラインをあわせてテキストを表示したい場合、style="position:abolute;top:0px;0px"のDIVの中にstyle="visibility:hidden;width:1px;height:120px"なダミーの画像と並べてテキストを書けば良い)。思いつくまでは大変だが、実装はとても簡単というまさにコロンブスの卵的な手法だ。


ianime.js でコジャレたホームページ作りにチャレンジ

 少し前にオープンソースとして公開したアニメーションライブラリianime.jsを使って、ちょっとしたアニメーション・エフェクトを持つホームページを作ろうとしているところだが、すべてのブラウザーできちんと表示しようとすると色々と細かな問題点に行きあたる。

 作成中のページへのリンク

 まず、一つ目の問題はIE6.0が持つ「同じURLを指すimgタグをinsertHTMLで複数追加するとキャッシュが働かずに何度もサーバーに取りに行ってしまうというバグ」(詳細)。このバグのために、すべてのimgタグが準備できるまでに余計に時間がかかってしまい、アニメーションが歯抜けになってしまう(ちなみに、IE7でこのバグがなおっているかどうかは未確認。上のリンクをクリックして結果を報告していただけると大変ありがたい)。もし、このバグを回避する方法をご存知の方がいらしたら、ぜひとも教えていただきたい。

 もう一つは、フォントのベースラインのアラインメント。画像とオーバーラップさせた"Life is beautiful"の文字のベースラインと7列目の画像の下のラインをきっちりと会わせたいのだが、画像も文字もposition:absoluteで置いていることもあり、インラインで並べた時のように自動的にベースラインを会わせてくれる訳ではなく、そこの解決方法が分からない。たかがベースラインと言えばそれまでなのだが、一度気になりだすとどうしても気になる性格なのだ。

 「フラッシュで作る」というのも一つの方法ではあるのだが、このくらい軽いアニメーションはJavascriptでするべきだと最近は考えているので試みているしだいである。


HTML+JSでプレゼン:高橋メソッドとかモンタメソッドとか

 YAML版でのソースコードはこんな感じ

==New features
*Image Slide
*Takahashi Method
*Monta Method

==Image Slide
img*images/grass.jpg

===Takahashi Method

==Monta Method
**{Mino Monta}* invented this method
*Hide a few *{keywords}* first
*Click those hidden words to show them

==Status
*Pre-alpha
*Not ready for public consumption
*Source code is so dirty
*Public beta is coming *{hopefully}* soon

HTML+JSでプレゼン、YAML版も作ってみた

 一つ前のエントリーに対して、アップルの増井さんから「もうちょっとシンプルな『メタ言語』で良いんじゃないでしょうか。YAMLみたいなのとか。」という良いアイデアをいただいたので、早速実装。先のサンプルが、ページ内にこんな感じで直接記述できる。

==iAnime.js: Benefits
*Small footprint (<6KB compressed)
*Lightweight (runs fine on iPhone)
*Works well with jQuery.js and prototype.js
*Free (MIT license)

==iAnime.js: Power
*Easy to use (only three methods)
*Powerful JSON-based animation language
*Extensivle ("effects" and "plug-ins")

==iAnime.js: Design principle
*KISS (Keep it simple stupid)
*Less is more
*Single timer for multiple animations
*Optimized for iPhone/iPod touch

 この方がずっと良い(増井さんに感謝!)。下にサンプルページを貼付けておいた(クリックすると次のページに進む)。ちなみに、YAML版を実装するのに一番苦労したのが、innerHTMLで取得したYAMLをsplit()で一つ一つの文に分割する部分。なぜかIEだけが勝手に文をくっつけてしまうので、なかなかうまく動かなかったのだ。ググっても答えが見つからずしばらく悩んでいたのだが、フと思いついて、YAML文を<pre>タグの中に入れたところ解決。しばしば答えはそんな所にある。


プレゼン資料用のメタ言語ってどうだろう

 メインマシンをMacBookに変えてから、プレゼン資料を作るためにいちいちParallelsからPowerpointを立ち上げるという作業がうっとうしくなって来た今日この頃である。もちろん、Keynoteを使うという方法もあるがファイルの互換性の問題があるし、Google Docsでは見栄えが悪い。

 「誰かがすでに作っているだろうな」と思いつつHTML+CSS+JavaScriptで作ってみたのがこれ(クリックすると次のスライドに進む)。

 手持ちのバージョンのSafari、Opera、Firefox、IEでは動作確認済み。iPhoneでももちろんちゃんと動く。フォントの大きさはダイナミックに計算しているので、全画面でブラウズしてもそれなりに動く。

 まだまだ機能的に不足している部分はたくさんあるが、こんな形で作っておけば、ブログでの公開も簡単だし、見た目はCSSしだいでなんとでもできる。ある程度形が整ってくればオープンソースのツールとして配布しても良い。

 ちなみに、プレゼンデータそのものをHTML/Javascriptから切り離したかったので、簡単な「プレゼン資料記述言語」を作った。上の資料はこんな感じで記述できる。

[
    {
      title: 'iAnime.js: Benefits',
      duration: 1,
      1: 'Small footprint (<6KB compressed)',
      2: 'Lightweight (runs fine on iPhone)',
      3: 'Works well with jQuery.js and prototype.js',
      4: 'Free (MIT license)'
    },
    {
      title: 'iAnime.js: Power',
      1: 'Easy to use (only three methods)',
      2: 'Powerful JSON-based animation language',
      3: 'Extensivle ("effects" and "plug-ins")'
    },
    {
      title: 'iAnime.js: Design principle',
      1: 'KISS (Keep it simple, stupid)',
      2: 'Less is more',
      3: 'Single timer for multiple animations',
      4: 'Optimized for iPhone/iPod touch'
    }
]

 私自身は、こういうアプローチはけっこう好きだが、これが一般受けするかどうかはいまいち不明なので、とりあえずここに公開して意見を集めてみようかと思う。もし評判が良ければ本腰を入れてオープンソースのプロジェクトを立ち上げてもよし、そうでなければ自分専用に地道に拡張して行くだけのこと。

【追記】YAML版も作ったのでそちらもどうぞ。


スライドするUIを実現するiSlider.js

 iAnime.jsのテスト用に作っていたスライドするUI。書いているうちにライブラリ化したくなったので、これもやはりMITライセンスとしてオープンソース化(Google Code iAnimejsよりダウンロード化)。たて・よこ・ななめ、自由にスライドさせることができることが特徴。"easein", "bounce"などのエフェクトも利用可能。

 まずはごく基本的なカード型のUIで横にスライドするもの。


 こんどは同じコンテンツを縦にスライドさせるもの。アコーディオンUIと呼ばれることもある。エフェクトに"bounce"を使っている点に注目。

 軽いのでiPhone/iPod touchでもサクサクと動くのが特徴。


YUI Compressor が圧縮しやすいコードを書くテクニックに関する一考察

 そもそもが超軽量に作られているiAnime.jsだが、YUI Compressorで圧縮するとどのくらいまで小さくなるものか試してみた。ianime028.js, ianime028ex.js, ibrowse010.js の三つのファイルを繋いでから圧縮すると、12739バイトが6629バイトに。約50%だ。一通りのテストをしてみたところ順調に動いているようで、やはり「安全性と圧縮率のバランスが良い」という評判は本当のようだ。

 ということで、これからはバージョンアップごとにこの圧縮したバージョンもリリースすることにした。とりあえず、version 0.28 は ianime028-all.js として Google Code のダウンドーロページにアップロードしておいたので、ぜひともお試しいただきたい。

 ちなみに、この過程でいくつかさらに小さくする方法を見つけたので(以下に記述)、version 0.29 では「6KByte以下に圧縮する」のに挑戦してみようと思う。

 ◇ ◇ ◇

 YUI Compressor で圧縮したコードを見ると分かるが、ローカルでしか参照されない関数や変数名をAとかBという一文字の変数に変換して文字数を節約している。当然だがグローバル変数の場合、そんなことができないのだが、何度も参照されるグローバル変数(もしくはそのプロパティ)の値を一度ローカル変数に代入しておいて、それを使ってアクセスするようにすれば、圧縮率が高まる。

 具体的には、iAnime.jsの場合だと、グローバル変数iAnimeのプロパティ"effects" (つまり"iAnime.effects")を何度も参照しているので、クロージャの内側で一度、

 var iAnimeEffects = iAnime.effects;

としてそれを使う様にすれば、YUI Compressor が  iAnimeEffects を一文字のローカル変数に置き換えてくれるので圧縮率が高くなるのである。この手の工夫は、コードのメインテナンスしやすさとのトレーオオフになることもあるのでやりすぎは必ずしも良くないが、変数名で妙な工夫をしなくて良い分、このテクニックは結構使えると思ったしだいである。


iAnime.js のドキュメントがようやく完成

 v.28でようやく一段落したiAnime.jsだが、ドキュメントもようやく数日遅れで完成。コードを書くのは楽しいが、ドキュメントを書くのはけっこう大変だ。まだまだやりたいことはたくさんあるのだが、コードばかり書いているとドキュメントが大幅に遅れてしまうので、ここで一度キャッチアップ。

 ちなみに、ドキュメントは以下の4ページから成り立っている。

 質問や要望はこのブログでも受け付けるし、Googleグループのianimejsグループでも受け付ける。あと、コードそのものに対するコメントも大歓迎(最新版は、Google CodeのiAnime.jsプロジェクトページからダウンロード可能)。

 ちなみに、現状で動作確認が出来ているのが、パソコン用でSafari, Firefox, IE, OperaとiPhone/iPod touch向けのSafari, そしてWii上のOpera(iFreecellはWiiでも遊べる)。日本の携帯のフルブラウザNetFrontでも動くとの報告を受けているが、私自身は確認していない。

 オープンソースは初体験でまだまだ感触がつかめていないが、プロジェクト管理そのものはGoogle Codeのおかげでずいぶんと楽が出来ている。svnはとても助かる。

 今後の展開としては、IEサポートのために一度ははずしたcanvasの活用を復活させてベクターグラフィックスの機能を再び追加する、シューティング・ゲーム用に衝突検知の仕組みを加える、なども考えているのだが、iAnime.jsそのものを大きくしたり複雑にせずにそういった機能をプラグインとして追加して行くことが鍵だと考えている。私の場合、コードの大きさにはコンフォート・ゾーンのようなものがあり、そこを超すと嫌いになってしまうことが多いからだ。


Javascript、クロージャを使ったプライベート関数の隠蔽について

(このエントリーは「Javascriptクイズ:無名関数と実行効率の話」の続編。)

「???」と頭をかしげる太郎に、「じゃあ、これだったらどうかな?」と三郎はコードを書き始めます。

function code2name(code)
{
   var mapping = {
      'us': 'United States',
      'ja': 'Japan',
      'ko': 'Korea',
      'ru': 'Russa',
      'uk': 'United Kingdom',
      'fr': 'France',
      'cc': 'China',
      'gw': 'Germany'
   };
   return mapping[code] || '(unknown)';
}

「カントリーコードを国名に変換しているんですね。」と太郎。

「どこが問題だか分かる?」

「うーん、マッピングのためのオブジェクトを毎回作り直しているところかな。」

「そうだね。code2nameが呼ばれるために毎回同じオブジェクトを作り直すのは無駄。一度だけ作って使い回しをするべきだよね。インタープリタが賢ければある程度はオプティマイズしてくれるかも知れないけど、色々なブラウザーで走らせることを考えれば、そんなものに頼るのは良くないよね。ちなみに、確か太郎はC++には強かったけど、C++だったらこんなときどうする?」

「"const static"を付けて明示的に共有するようにコンパイラに指示する、かな」

「そうだね、C++の場合はstaticを指定することにより、同じ変数を使い回すようにコンパイラに指示ができるんだよね。Javascriptの場合、同じオブジェクトを使い回したい場合にはクロージャを使うんだ」とコードを書き直す三郎。

var code2name = (function(){
   var mapping = {
      'us': 'United States',
      'ja': 'Japan',
      'ko': 'Korea',
      'ru': 'Russa',
      'uk': 'United Kingdom',
      'fr': 'France',
      'cc': 'China',
      'gw': 'Germany'
   };
   return function(code) { return mapping[code] || '(unknown)';};
})();

「こうしておくと、一番外側の無名関数は一度だけ実行されて、その時にマッピングのためのオブジェクトが作られる。この時点ではローカル変数mappingだけがそれを参照しているんだけど、この関数がそれを参照する別の無名関数を作って返し、それへの参照がグローバル変数code2nameに格納されるため、結果的にはマッピング用のオブジェクトは外側の無名関数の終了後も生き延びることになるんだ。」

「ローカル変数が関数の終了後も生き残るんですか。クロージャってゾンビみたいですね。」と太郎。

「便利だろクロージャって。こんな風にクロージャを使えば、C++のstatic変数のようにオブジェクトの共有ができるようになるし、オブジェクト指向で言うところの『隠蔽』もちゃんとできるってわけさ。」と三郎。

「つまり、最初の例でもcapitalize関数をクロージャを使って隠蔽すれば良いってことですね。自分でやってみます!」と太郎が書いたのがこのコード。

var style2prop = (function(){
    function capitalize(str)
    {
        return str.charAt(1).toUpperCase();
    }

    return function(str) {
        return str.replace(/-[a-z]/g, capitalize);
    }
})();

「太郎もクロージャを理解しはじめたみたいだね。関数もオブジェクトだってことを理解していれば、毎回まったく同じものを作り直すのは無駄だって言うことは分かるよね。先の例と同じ様に、インタープリタがある程度はオプティマイズしてくれるかも知れないけど、可能であればこんな風に明示的に関数の共有を指示すべき。関数だってオブジェクトなんだから、毎回作り直すべきか使い回すべきかは常に意識してプログラムを書く習慣を持つべきだよね。」

「そうですね。でも、関数オブジェクトの場合にも『毎回作り直した方が良いケース』なんてあるんですか?」

「良い質問だね。クロージャを使うケースがそうだよ。タイマーを使ってオブジェクトのメソッドを呼び出したい時は、こんなコードを書くよね。」

    var obj = new ...; // local variable
    ...
    setInterval(function() {obj.callback();}, 33);

「ここでsetIntervalに渡すために作っている無名関数だけど、メソッドを呼びたいオブジェクトへの参照を関数オブエジェクトそのものに持たせる必要があるんで、実際に毎回違う関数オブジェクトを作る必要があるんだ。こういう時にこそ、動的に生成する無名関数オブジェクトが力を発揮するんだ。」

「うーん、理解できたようなできないような」と太郎。

「関数オブジェクトとクロージャの概念は、C++から来た人たちにはなかなか直感的に理解できないから、太郎の悩みは当然だよ。でも、この障壁を乗り越えて、関数オブジェクトとクロージャを適切に使いこなせるようになることは、Javascriptにおけるオブジェクト指向プログラミングをする上で必須だっていうことを覚えておくと良いよ。C++だってstatic, private, constなどのキーワードがちゃんと使いこなせるかどうかが良いオブジェクト指向のプログラムを書けるかどうかの重要な鍵じゃないか。それと同じさ。」と三郎。

「そうだ、クロージャへの理解を深めるために一つ宿題をあげよう」と続ける三郎。「今の、"font-style"を"fontStyle"に変更する関数style2propに加えて、"font_style"を"fontStyle"に変更する別の関数hoge2propを同じライブラリの中に作りたいとき、その二つの関数でcapitalizeを共有しつつ、かつそれをクロージャを使って隠蔽するにはどうしたら良いか考えてみると良いよ。」と言って立ち去ってしまう三郎。

 ということで、今回の宿題は、一つのプライベートな関数を複数のパブリックな関数で共有しつつ、そのプライベート関数をクロージャを使って隠蔽するテクニック。さあ、太郎はどんな答えにたどりついたでしょう?


iaime.js v0.28: style-tweenを追加

 ようやく期末試験もメドが付いたので欲しかったstyle-tween機能をiAnime.jsに追加(v0.28としてGoogle code/ianimejsにリリース済み)。使い方は下の例を見ていただければ自明だと思うが、アニメーション前のスタイルと後のスタイルをfrom/toで指定するとその間を指定した時間で変化させる。JSON形式で渡すため、スタイルシートとは少し異り、値の方は必ず"か'で囲う必要があるし、スタイル名が'-'を含む場合も"か'で囲う必要がある。色のフォーマットは#rrggbb形式のみサポート。

   anime.add({
       element:this,
       effect:'style',
       from: { background: '#eeffff', 'font-size':'10pt',
               width:'20pt', height:'14pt', margin:'1pt', 'padding-top':'0pt' },
       to:   { background: '#80ffff', 'font-size':'14pt',
               width:'26pt', height:'20pt', margin:'-2pt', 'padding-top':'1pt' },
       duration: 200
   });

 テスト用に作ったのが、Wii風のバーチャルキーボード。マウスを上で動かすと何とも気持ちが良い(クリックしても何もしない)。