2011年12月23日金曜日

【Titanium Advent Calendar 2011:23日目】TiでMetro UIぽい動きを再現してみる

ども!さいきん「スタートアップ」と聞くと、がんばって飛んだはいいけれど、どこまでいっても着地できなかった「風船おじさん」的なニュアンスを感じてドキドキしてしまう@toru0325ことトールです。こんにちは。

今回は@astronaughtsさんの企画「Titanium Advent Calendar 2011」に参加ということで、めったに書かないブログを書いてみます。いやあ、こういうイベントでもないと、なかなか書くモチベーションがないもので。コンスタントにアウトプットできる人を心底尊敬します。というか、昨日の@goandoさんのモジュールがインパクトありすぎてこまってますw。

さておき、今年はTitaniumにかなりお世話になりました。Tiに出会ったのはたしかSDK1.4の頃ですから、1年ちょっとの付き合いですかね。Titaniumのおかげでモバイル開発に自信がついたと思いますし、たくさんの人との出会いがありました。本当にいいツールだと思います。ちなみに今年リリースしたアプリはこちら。

もしよければインストして遊んでみてやってくださいませ。

TiでWindows PhoneのMetro UIぽい動きを再現してみる(for iOS)

さて本題。本当は20日の@astronaughtsさんのこのネタの続きで、Pathのメニューボタンを実装してみるというホットな話題をやろうとしたのですが、どうやら海の向こうですでに誰かがやってるという噂。さらにはAppcelerator CEOのジェフが直々にモジュールを作っているらしいじゃないですか。ずるい、ずるいわジェフ。もう怒ったので、今回はWindows PhoneのMetro UIぽいアニメーションをTitaniumで再現してみることにしました。

ちなみにWindows Phoneはもっていませんので、正確な動作はよくわかりません。Youtubeの動画やHTML5で作られたハンズオンデモあたりを参考に、見よう見まねで実装してみたいと思います。細かい挙動はやや曖昧ですが、そこはなに、雰囲気で。

あ、iOSオンリーとなります。アンドロイダーな方々、ほんとうにすいません。

完成品はこちら

そんなわけで、とりあえずこんな感じに仕上がりました。 なんとなく雰囲気でてますかね?

デザインエレメントは時間がなかったので、タイルもリストもすべてダミー画像を作って貼りつけています。本当はタイル内のアニメーションもやりたかったのですが、さすがに無理でした。。

Metro UIの何を実現するか

今回は時間もないので、次の要素にしぼって実装を考えました。

  1. ホーム画面のタイルのイン&アウトアニメーション
  2. Outlookを起動したときのイン&アウトアニメーション
  3. ホーム右メニューリストへのスライドアニメーション

とりあえず1のホーム画面のフワッとしたタイルアニメーションは必須ですよね。さらに2のOutlook画面はバックが急フェードで白に切り替わるダイナミックさが素敵なのでぜひやってみたいと思いました。3はおまけです。余力があれば。

どういう実装にすれば良い?

まず1のホーム画面の「タイルアニメーション」。こいつをどこに実装すればよいか考えました。おそらくタイル全体がひとつのViewとして、アニメーションと一緒に管理されているのがベストなんでしょう。しかしながらタイルがマトリックス状に配置されるのでTableViewでは役不足そうです。そこでScrollViewをメインに使い、その中でViewをいい案配に配置してアニメーションをさせるのが良いと判断しました。ScrollViewを拡張したMetroMatrixViewなるものを実装すると良さげです。

  • 複数タイル(View)を内包するMetroMatrixView(ScrollViewを拡張)を実装し、イン&アウトアニメーションを実行する

ほい、これでホーム画面はいけそうです。

それともう一つ、Outlook画面はその白い背景もろともアニメーションしてアウトします。これはViewだけでは実現できそうにないですね。そこで、Windowを拡張し、そこでアニメーションを管理させます。MetroWindowと名付けましょう。

  • というわけで、実装の方向性としては、MetroWindow(Windowを拡張)を実装し、ウィンドウアニメーションを実行する

うん、この2本立てでなんとかなりそうです。

MetroMatrixViewとMetroWindowの使いかた

やや複雑になりそうだったので、最初から外部jsファイルにして、CommonJS風味でrequireしてやることにしました。ファイル名を「MetroUI.js」にしたので、

//=== MetroUI.jsを読み込む
var MetroUI = require("MetroUI");

という具合に今風に読み込めます。「MetroUI.js」ではexports下にMetroMatrixViewとMetroWindowがぶら下がっていますので、適時newして使うことができます。べんりですね。最近ようやくシングルコンテキスト+CommonJSのメリットがわかってきましたw。

さて、MetroMatrixViewはこのようにnewして使用します。

//=== 用意したタイルビューリストからMetroMatrixViewを生成
var mv = new MetroUI.MetroMatrixView({
    views : views,
    cellWidth : 114,
    cellHeight : 114,
    margin : 10,
    column : 2,
    width : 260,
});

初期パラメータで大事なのはviewsです。これは全タイルビューのリストです。newする前に用意しましょうね。ここではただのダミー画像Viewを使用しています。

//=== 各タイルビューを用意
var views = [];
for(var i = 0; i < 10; i++) {
    var v = Ti.UI.createView({
        backgroundImage : "src/home_dummy_0" + ((i % 7) + 1) + ".png", //=== ダミー画像を使用
    });
    v.addEventListener("click", _clickCell);
    views.push(v);
}

MetroMatrixViewをnewすると返されるのは拡張されたScrollViewです。こいつにはext(拡張)オブジェクトがついているため必要なときに呼びだせます。

//=== ウィンドウがアクティブになったら自動でFlipInするように設定
w.addEventListener("focus", function() {
  mv.ext.flipIn();
  _showButton();
});

MetroWindowのほうも使い方は同じです。次のように通常のWindowと同じようにインスタンス化します。

//=== MetroWindowとして生成
var w = new MetroUI.MetroWindow();

あとは必要なときにext内のメソッドを呼びます。こんな感じで。

//=== ウィンドウをフェードイン
w.ext.fadeOpen();

さて、本当ならMetroMatrixViewについて詳しく解説したいところなのですが、さすがに長くなり過ぎるということで、ざっくりと割愛します。すいませんすいません、気になった方はソースみてください。

その代わり今回気づいたポイントを絞って書きますね。

3Dアニメーション実装で気をつけたいところ

3D表現で気をつけたいところは「奥行きの出し方」です。

たとえばMogSnapやMogSmashなどの2Dアニメーションでは、Viewを回転しようとおもえば、

view.animate({
    duration: 300,
    transform: Ti.UI.create2DMatrix().rotate(180)
});

とやればすんなりできるので、3Dも同じように、

view.animate({
    duration: 300,
    transform: Ti.UI.create3DMatrix().rotate(180, 0, 1, 0) // Y軸のまわりを回転
});

とやれば勝手にフリップアニメーションしてくれると思ったのですが、残念ながら、これだとページがめくれる感じでなく、横方向にサイズが伸び縮みするだけのアニメーションになります。つまり奥行きがまったく表現されない状態です。

僕もここでややハマりました。3DMatrix内に奥行きを設定するプロパティがあるはずと思ったのでKitchenSinkを眺めると、3DMatrixのm34プロパティをいじっているじゃないですか。どうやらここがポイントのようです。いろいろテストした結果、次のようにすることでうまく奥行きを出すことができました。

var t = Ti.UI.create3DMatrix();
t.m34 = -1 / 400;
view.animate({
    duration: 300,
    transform: t.rotate(180, 0, 1, 0) // Y軸のまわりを回転
});

これさえできればあとは各viewの変形の基準点である「anchorPoint」を設定すればよいわけです。たとえば、

view.anchorPoint = {
    x: 0.0,
    y: 0.5
};

とすることで、viewの左端を起点にドアのように開く動作にすることができます。

まとめ

なんだかとりとめもない感じになってしまいましたが、いかがでしたか?3Dアニメーションは使いどころがなかなか難しいですが、効果的にやれば突き抜けた個性をだすことができそうです。つかいどころや演出方法ふくめ、個人的にさらに研究したいと書きながら思いました。

さて明日は24日。@toweroflさんです。どんな内容か楽しみですね!

ソースファイル

ソースファイルをGithubに置きました。興味あるかたはどぞどぞ。

https://github.com/toru0325/TiMetro

0 件のコメント:

コメントを投稿