フェードイン/アウトで画面の切り替える

今回はちょっと見栄えの良い画面転換ということでフェードイン/アウトを実装してみます。

アプリケーションの起動はこちらから。

ページ単位でシーングラフを作成

まず、切り替える単位でグループ化しておきます。
単体であればグループ化する必要もありませんが、拡張性を考えればグループ内に配置しておく方が良いでしょう。

var page1 = Group {
    opacity: bind 1.0 - opacity
}
var page2 = Group {
    opacity: bind opacity
}
var opacity = 0.0;

今回は2枚を交互に切り替えるだけなので、それぞれの透明度をbindしました。一方が見えているときは、もう一方が見えない事になるので、page1はbindに式を用いています。
初期状態では、page1のみが表示されている状態です。

アニメーションの定義

アニメーションのTimelineは2つ作成し、片方を0から1.0へ、もう1方を0.0へと線形的に2秒間でopacityを変化させます。

var timeline_forward = Timeline {
    repeatCount: 1
    keyFrames : [
        KeyFrame {
            time : 2s
            values: [ opacity => 1.0 tween Interpolator.LINEAR ]
        }
    ]
}
var timeline_backward = Timeline {
    repeatCount: 1
    keyFrames : [
        KeyFrame {
            time : 2s
            values: [ opacity => 0.0 tween Interpolator.LINEAR ]
        }
    ]
}

ここでは、この後のボタン制御でコントロールしているためにキーフレームは1つのみ定義しています。

ボタンの処理

アニメーションが実行中は処理が行われないように制御し、後は現在のopacity の値によってどちらのアニメーションを実行するかを定義しているだけです。

            Button {
                text: "Click"
                action: function() {
                    if (timeline_forward.running or timeline_backward.running) {
                        return;
                    }
                    if (opacity == 0.0) {
                        timeline_forward.playFromStart();
                    } else {
                        timeline_backward.playFromStart();
                    }
                }
            }

月と星

月は円を2つずらして作成、これは簡単です。
星はちょっと面倒なので、クラスを定義して座標計算します。

class Star extends Polygon {
    var centerX: Number;
    var centerY: Number;
    var radius: Number;
    var sita: Number = 72 * 5 / 180;
    override var points = bind [
        centerX,                              centerY - radius,
        centerX - radius * Math.sin(2*sita) , centerY - radius * Math.cos(2*sita) ,
        centerX - radius * Math.sin(sita)   , centerY + radius * Math.cos(sita) ,
        centerX + radius * Math.sin(sita)   , centerY + radius * Math.cos(sita),
        centerX + radius * Math.sin(2*sita) , centerY - radius * Math.cos(2*sita) ,
    ];
}

五芒星の頂点を結んでおり、bindで動的に変化するように定義しています。こうすれば中心座標と半径さえ指定すれば簡単に五芒星を記述できます。回転させたい場合は、例のごとく『最後に』rotateしてください。

ついでにこれをランダムにたくさん配置します。これにはfor式を利用すると簡単です。

    content: for (i in [0..10]) {
        Star {
            centerX: Math.random() * 200, centerY: Math.random() * 200
            radius: 8
            fill: Color.YELLOW
        }
    }

[0..10]は[0,1,2,....,9] というシーケンスと同じ意味です。したがってforは10回ループしています。
各ループではStarのインスタンスを生成し、ブロックの最後に宣言された値のシーケンスをfor式が返す為、Starのインスタンス10個のシーケンスがcontentに設定されるわけです。後は適当に座標を散らしているだけです。

ソース全体

ソース全体です。

/*
 * FadeInOutScreen.fx
 *
 * Created on 2009/08/24, 23:14:23
 */

package snipets;

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;

import javafx.scene.Group;
import javafx.util.Math;
import javafx.scene.shape.Polygon;
import javafx.animation.Timeline;
import javafx.animation.KeyFrame;
import javafx.animation.Interpolator;
import javafx.scene.control.Button;

var page1 = Group {
    content: [
        Circle {
            centerX: 100, centerY: 100
            radius: 40
            fill: Color.YELLOW
        },
        Circle {
            centerX: 120, centerY: 100
            radius: 40
            fill: Color.BLACK
        },
    ]
    opacity: bind 1.0 - opacity
}
class Star extends Polygon {
    var centerX: Number;
    var centerY: Number;
    var radius: Number;
    var sita: Number = 72 * 5 / 180;
    override var points = bind [
        centerX,                              centerY - radius,
        centerX - radius * Math.sin(2*sita) , centerY - radius * Math.cos(2*sita) ,
        centerX - radius * Math.sin(sita)   , centerY + radius * Math.cos(sita) ,
        centerX + radius * Math.sin(sita)   , centerY + radius * Math.cos(sita),
        centerX + radius * Math.sin(2*sita) , centerY - radius * Math.cos(2*sita) ,
    ];
}
var page2 = Group {
    content: for (i in [0..10]) {
        Star {
            centerX: Math.random() * 200, centerY: Math.random() * 200
            radius: 8
            fill: Color.YELLOW
        }
    }
    opacity: bind opacity
}
var opacity = 0.0;
var timeline_forward = Timeline {
    repeatCount: 1
    keyFrames : [
        KeyFrame {
            time : 2s
            values: [ opacity => 1.0 tween Interpolator.LINEAR ]
        }
    ]
}
var timeline_backward = Timeline {
    repeatCount: 1
    keyFrames : [
        KeyFrame {
            time : 2s
            values: [ opacity => 0.0 tween Interpolator.LINEAR ]
        }
    ]
}
/**
 * @author shuji
 */
 Stage {
     title : "Fade In/Out"
     scene: Scene {
         width: 200, height: 200
         fill: Color.BLACK
         content: [
            page1,
            page2,
            Button {
                text: "Click"
                action: function() {
                    if (timeline_forward.running or timeline_backward.running) {
                        return;
                    }
                    if (opacity == 0.0) {
                        timeline_forward.playFromStart();
                    } else {
                        timeline_backward.playFromStart();
                    }
                }
            }
         ]
     }
 }