LoginSignup
12
12

More than 5 years have passed since last update.

tmlib.js で気持ち良い変形アニメーションするボタンを作ってみた

Last updated at Posted at 2015-03-12

コリスさんの 『[CSS]これから増えてきそうなMaterial Designにぴったり!気持ちいいアニメーションで表示させるモーダルコンテンツ
ってやつを見て良いなと思ったので tmlib.js でも作ってみました!!

画面遷移とかにつかうと便利ですね♪

Demo

ボタンを押すとサークルに変形して画面いっぱいに広がります.
もう一度クリックすると元に戻ります.

runstant

Code

/*
 * # tutorial - tmlib.js
 * tmlib.js のチュートリアルです.
 * http://phi-jp.github.io/tmlib.js/tutorial.html
 */

var SCREEN_WIDTH    = 640;              // スクリーン幅
var SCREEN_HEIGHT   = 960;              // スクリーン高さ
var SCREEN_CENTER_X = SCREEN_WIDTH/2;   // スクリーン幅の半分
var SCREEN_CENTER_Y = SCREEN_HEIGHT/2;  // スクリーン高さの半分
var ASSETS = {
    "player": "http://jsrun.it/assets/s/A/3/j/sA3jL.png",
    "bg": "http://jsrun.it/assets/a/G/5/Y/aG5YD.png",
};

// main
tm.main(function() {
    // キャンバスアプリケーションを生成
    var app = tm.display.CanvasApp("#world");
    // リサイズ
    app.resize(SCREEN_WIDTH, SCREEN_HEIGHT);
    // ウィンドウにフィットさせる
    app.fitWindow();

    // ローダーで画像を読み込む
    var loading = tm.ui.LoadingScene({
        assets: ASSETS,
        width: SCREEN_WIDTH,
        height: SCREEN_HEIGHT,
    });

    // 読み込み完了後に呼ばれるメソッドを登録
    loading.onload = function() {
        // メインシーンに入れ替える
        var scene = MainScene();
        app.replaceScene(scene);
    };
    // ローディングシーンに入れ替える
    app.replaceScene(loading);

    // 実行
    app.run();
});

// シーンを定義
tm.define("MainScene", {
    superClass: "tm.app.Scene",

    init: function() {
        this.superInit();

        // モーフィングボタンを生成
        var button = MorphingButton({
            fillStyle: "hsl({0}, 60%, 50%)".format(Math.rand(0, 360)),
            cornerRadius: 16,
            fontSize: 48,
            text: 'Button',
        }).addChildTo(this);

        button.setPosition(SCREEN_CENTER_X, SCREEN_CENTER_Y);
        // 円が広がりきった際のイベント
        button.oncircled = function() {
            this.app.pushScene(TestScene());
            // シーンが戻ってきたらボタンも戻す
            this.one('resume', function() {
                button.morphDefault();
            });
        }.bind(this);

        button.onpointingover = function() {
            this.app.element.style.cursor = 'pointer';
        }.bind(this);
        button.onpointingout = function() {
            this.app.element.style.cursor = 'auto';
        }.bind(this);
        button.onpush = function() {
            this.app.element.style.cursor = 'auto';
        }.bind(this);
    },

    update: function(app) {
    }
});

tm.define("TestScene", {
    superClass: "tm.app.Scene",

    init: function() {
        this.superInit();

        var label = tm.display.Label('tmlib.js でモーフィングボタンを\n作ってみたよ♪').addChildTo(this);
        label.setPosition(SCREEN_CENTER_X, SCREEN_CENTER_Y);
        label.alpha = 0;
        label.tweener
            .fadeIn(200);
    },

    onpointingstart: function() {
        this.app.popScene();
    }
});


tm.define("MorphingButton", {
    superClass: "tm.ui.FlatButton",

    init: function(param) {
        this.superInit(param);

        this.initialWidth = this.width;
        this.initialHeight = this.height;
        this.initialCornerRadius = this.cornerRadius;

        this.on('push', function() {
            this.morphCircle();
        });
    },

    // 円形に変形
    morphCircle: function(radius) {
        var circleSize = radius || 1200;

        this.setInteractive(false);
        this.label.tweener
            .clear()
            .fadeOut(200)
            ;
        this.tweener
            .clear()
            .wait(300)
            .to({
                width: this.height,
                cornerRadius: this.height/2
            }, 300, 'easeOutQuint')
            .to({
                width: circleSize,
                height: circleSize,
                cornerRadius: circleSize/2
            }, 500, 'easeOutQuint')
            .call(function() {
                this.flare('circled');
                this.setInteractive(true);
            }, this)
            ;
    },

    // 元の形に変形
    morphDefault: function() {
        this.setInteractive(false);
        this.label.tweener
            .clear()
            .wait(800)
            .fadeIn(200)
            ;

        this.tweener
            .clear()
            .to({
                width: this.initialHeight,
                height: this.initialHeight,
                cornerRadius: this.initialHeight/2
            }, 500, 'easeOutQuint')
            .to({
                width: this.initialWidth,
                cornerRadius: this.initialCornerRadius,
            }, 300, 'easeOutQuint')
            .call(function() {
                this.flare('defaulted');
                this.setInteractive(true);
            }, this)
            ;
    },
});

Tips

押したときにイベント登録

今回は MorphingButton というクラスを定義して使っています.
MorphingButton は tm.ui.FlatButton を継承しているので押すと push イベントが発火します.

init 時に push イベントを登録し, ここで押されたら円形に広がるという処理を登録しています.

        this.on('push', function() {
            this.morphCircle();
        });

良い感じのタイミングでシーン遷移

ボタンを生成している側で, 広がりきった際に発火するイベント circled
関数を登録しています.

ここで, TestScene を pushScene() しています.
そして pushScene したシーンが破棄されたら, ボタンが置いてあるシーン TestScene 側で
resume イベントが発火するのでボタンを元に戻す関数 morphDefault() を呼んでいます.

これでキレイにアニメーションしながら画面遷移することができます.

        // 円が広がりきった際のイベント
        button.oncircled = function() {
            this.app.pushScene(TestScene());
            // シーンが戻ってきたらボタンも戻す
            this.one('resume', function() {
                button.morphDefault();
            });
        }.bind(this);

以上, 備忘録でした!

12
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
12