昨年末に公開したRaNa design WEBSITE RENEWAL PROJECTの舞台裏エントリーその2です。その1の後に読んでいただくとよりお楽しみいただけます。
今回からはJavaScriptの実装についてより深く踏み込んでいきますが、全体のコードは長大なので、要点と思える部分だけ切り取っていく感じでお話できればと思っています。
JavaScriptのフレームワーク
jQueryを使いました。これ以降の文章でjQueryのメソッド名とかを普通に説明に使っているので、前提になる話として。
フルスクリーンのグラフィックコンテンツなのでRaphaëlなんかを使ってみたら面白いんじゃないかということで最初はこれを使ってごにょごにょ作業していたのですが、自分の習熟度が足りない上、スケジュールがあまりないということもあって馴染み深いjQueryだけでやることにしました。アニメーションは結局.animate()だけでやっています。
設計と実装編 その2 - ページの構造
アニメーションの話の前に、ページにどうやってフルスクリーンのコンテンツを乗っけるのかを説明します。簡単に言ってしまうとLightboxです。通常のHTMLから特定のイベントによってフルスクリーンのモーダルウィンドウを出し、その上にAjaxで読み込んだコンテンツを乗っけます。
モーダルウィンドウを出すスクリプトは以前自分で作ったものを改良して使いました。
"smartModalWindow" jQuery plugin
読み込むコンテンツは別のHTMLとして用意しておき、それをsmartModalWindowで読み込みます。
必要なページは、カエルが動き回る初期画面(screen.php)、Twitterにログイン後に参加登録する画面(registration.php)、キャラクターの検索画面(search.php)です。
トップページ(index.php)で全てのJSファイルを読み込み、init.jsによって処理を開始します。init.jsは
- モーダルウィンドウの呼び出し
- 画面に乗っかる看板(ナビゲーション)などの出し入れ
- screen.phpに関するインスタンスの作成、処理の実行
- cookieの処理
などを行っています。
基本的にはinit.js以外のJSファイルではクラスや、API用コールバック関数の定義をしているだけで、それを親となる別のファイルから利用している、という構造になっています。
Twitterとの連携については弊社yoshi-zouが、JSONPを生成するAPIを作ってくれましたのでこれを利用します。
例: http://kaelab.ranadesign.com/api/rnrn/list.php
$("<script/>", {
"src": "http://kaelab.ranadesign.com/api/rnrn/list.php"
}).appendTo("head");
こういう感じでscriptタグとして突っ込むと、特定のコールバック関数が実行されるというもの。上記の例では
function getList(res) {
var data = res["block"],
result = res["result"];
if (result == 0) {
// error
} else {
// success
// script with data
}
}
こんな感じのコールバック関数を用意しておけば、データを受け取って処理ができます。データを受け取れさえすれば、おなじみの$.ajax()と同じノリでいけます。
設計と実装編 その3 - キャラクターのアニメーション
画面内のキャラクターは、専用のクラスのインスタンスとして実装します。Kaeruという名前のクラスを作り、prototypeのメソッドとしていろいろな処理をくっつけていきます。キャラクターが行う動作については全てこのクラスを通して行うようにします。
クラス名がKaeruっていうのは安直ですが、すいません、正直何も考えていませんでした。
function Kaeru(args) {
// 引数でTwitterデータと、Screenオブジェクトを受け取る
this.data = args.data;
this.screen = args.screen;
this.initialize();
}
Kaeru.prototype = {
conf: { }, // インスタンスの共通設定
initialize: function() { }, // 初期化処理
preloadCharacterImgs: function() {}, // キャラクター画像プリロード
moveInit: function() {}, // 初回の_move()
resetMove: function() {}, // 特定の位置から移動を再開(マウスアウト時)
_selectBehavior: function() {}, // behavior(エリアIDが並んだ配列)をランダムで選択して返す
_move: function() {}, // 実際にキャラクターを動かすメソッド。.animate()を使う
_getImgsrc: function() {}, // エリアからキャラクターの画像を選択して返す
_setBaloon: function() {}, // Twitterの吹き出しを生成
_showBaloon: function() {}, // Twitterの吹き出しを表示
_hideBaloon: function() {}, // Twitterの吹き出しを隠す
_computeDuration: function() {} // 次のアニメーションの終点までのdurationを計算して返す
};
別ファイルのscreen.jsでScreenというクラスを定義していて、こいつからKaeruクラスをインスタンス化して利用しています。「スクリーンの上にカエルが乗っている」というイメージです。ここまでの要件を満たすだけでこれだけの機能が必要になってしまいました・・・。自分にとって必然的にオブジェクト指向っぽい書き方で作った初めての案件かもしれません。
さて、アニメGIFの話をしましょう。弊社デザイナーの近藤が以下のようにバリエーションに飛んだ画像を作ってくれました。
縦横の移動だけで、1キャラクターにつき3つの画像が必要です。縦移動(上・下共通)、左へ移動、右へ移動の3種類です。これをエリアが切り替わる際に適当な画像に切り替えることで、キャラクターが縦横無尽に動いているように見せています。(縦横無尽・・・に見えていますよね・・・?)
切り替える画像は_getImgsrcメソッドで適切なものを判断して返しています。コードはこんな感じ。
_getImgsrc: function(map) {
var direction, imgsrc;
if ((map.fromX - map.toX) > 0) {
direction = "left";
} else if ((map.fromX - map.toX) < 0) {
direction = "right";
} else if ((map.fromY - map.toY) > 0) {
direction = "up";
} else {
direction = "down";
}
switch(direction) {
// 左に進む画像をimgsrcにセット
case "left" : imgsrc = this.characterImg.src.moveLeft; break;
// 右に進む画像をimgsrcにセット
case "right" : imgsrc = this.characterImg.src.moveRight; break;
// 上に進む画像をimgsrcにセット
case "up" : imgsrc = this.characterImg.src.moveUp; break;
// 下に進む画像をimgsrcにセット
case "down" : imgsrc = this.characterImg.src.moveDown; break;
}
return imgsrc;
}
引数mapのデータは前のエントリーで書いた、こんなデータです。
{ fromX: 810, fromY: -55, toX: 810, toY: 80 }
また、上に進む画像と下に進む画像は実際は同じ画像です。
_move()メソッドをざっくり説明すると
- 次に進むべきをエリアを選択(初期化時にコースは決定している)
- 適切なキャラクター画像を選択
- キャラクターを
.animate()
というような処理を行っています。これを再帰的に実行することでキャラクターがエリアを次々に移動します。
さて、長くなった割にうまくまとまっていませんが、めげずに「その3」に続く予定です。

コメントする