JavaFXにはアニメーションをプログラムするために,AnimationクラスとAnimationTimerクラスの2種類が用意されています.
初期状態,中間状態,終了状態とその間をつなぐ補間方法を指定することでアニメーションを作成します.ただし,アニメーションの設定を途中で変更することは出来ません.
set.CycleCount と setAutoReverseを使用して,アニメーションの繰り返しの設定が出来ます.
また,次の命令を使用することで,登録された複数のアニメーションを同時に,もしくは順番に実行できます.
| クラス | 内容 |
|---|---|
| SequentialTransition | 登録したアニメーションを順番に実行 |
| ParallelTraansition | 登録したアニメーションを同時に実行 |
予め用意されている遷移を利用して,同時に,または順番に実行される複数のアニメーションを作成できます.
| クラス | 内容 |
|---|---|
| FadeTransition | ノードの透明度を変化させるアニメーション |
| FillTransition | ノードの塗りつぶし色を変化させるアニメーション |
| PathTransition | ノードが経路に沿って移動するアニメーション |
| RotateTransition | ノードが回転するアニメーション |
| ScaleTransition | ノードが拡大・縮小するアニメーション |
| StrokeTransition | ノードの線の色が変化するアニメーション |
| TranslateTransition | ノードを平行移動させるアニメーション |
| PauseTransition | アニメーションを一時停止させる |
ノードのopacity変数を一定の間隔で更新することによって透明度を変化させます.
opacity変数の値は,fromValueが指定されている場合はそこから,それ以外の場合はノードのopacity値から開始され, toValue値が指定されている場合はそこで,それ以外の場合は開始値にbyValueを加えた値で停止します. また,toValueとbyValueの両方が指定されている場合は,toValueが優先されます.
次の例は,図形の色の透明度が変化します.
import javafx.animation.FadeTransition;
import javafx.util.Duration;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class FadeExample extends Application {
@Override public void start(Stage stage) {
stage.setTitle("Fade Transition");
//Group root = new Group();
VBox root = new VBox();
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(10,10,10,10));
root.setSpacing(50.0);
Scene scene = new Scene(root, 400, 400);
scene.setFill(Color.WHITE);
Circle circle = new Circle(100);
circle.setFill(Color.RED);
root.getChildren().addAll(circle);
stage.setScene(scene);
stage.show();
FadeTransition ft = new FadeTransition(Duration.millis(3000), circle);
\\Duration.millis(数値)には繰返し時間をミリ秒で指定する
ft.setFromValue(1.0);
ft.setToValue(0.1);
ft.setCycleCount(FadeTransition.INDEFINITE);
\\CycleCount()には繰り返す回数を指定する.無限に繰り返す時はINDEFINITE
ft.setAutoReverse(true);
\\AutoReverseは繰返しを往復にするかどうかをtrue,falseで指定する
ft.play();
}
public static void main(String[] args) {
launch(args);
}
}
translateXおよびtranslateY変数を一定の間隔で更新することによって,ノードを経路に沿って移動させます.経路には図形の輪郭を指定します.
rotate変数は,orientationがOrientationType.ORTHOGONAL_TO_TANGENTに設定されている場合に更新されます.
次の例は,円周上を移動するアニメーションを表示します.
import javafx.animation.PathTransition;
import javafx.animation.Interpolator;
import javafx.util.Duration;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class PathExample extends Application {
@Override public void start(Stage stage) {
stage.setTitle("Path Transition");
VBox root = new VBox();
root.setAlignment(Pos.CENTER);
Scene scene = new Scene(root, 400, 300);
scene.setFill(Color.WHITE);
Circle circlepath = new Circle(100);
Circle circle = new Circle(10);
circle.setFill(Color.RED);
root.getChildren().add(circle);
stage.setScene(scene);
stage.show();
PathTransition pt = new PathTransition();
pt.setDuration(Duration.millis(5000));
pt.setPath(circlepath);
\\Pathには辿らせる経路を指定する
pt.setNode(circle);
\\Nodeには動かす図形などを指定する
pt.setInterpolator( Interpolator.EASE_BOTH );
\\Interpolatorにはアニメーションの仕方を指定,以下のような指定ができる
\\EASE_BOTH:開始,終了ともに滑らかに速度が変わる
\\EASE_IN:開始がだんだん早くなる
\\EASE_OUT:終了がだんだん遅くなる
pt.setCycleCount(PathTransition.INDEFINITE);
pt.setAutoReverse(true);
pt.play();
}
public static void main(String[] args) {
launch(args);
}
}
shapeのfill変数を一定の間隔で更新することによって図形の塗りつぶしを変化させます.
fill変数の値は,fromValueが指定されている場合はそこから,それ以外の場合はshapeのfill値から開始され、toValueで停止します.
FillTransition ft = new FillTransition(Duration.millis(3000), circle,Color.RED,Color.BLUE);
ft.setCycleCount(4);
ft.setAutoReverse(true);
ft.play();
rotate変数を一定の間隔で更新することによってノードを回転させます.角度値は度で指定します.
rotate変数は,fromAngleが指定されている場合はそこから,それ以外の場合はnodeのrotate値が使用され,toAngle値が指定されている場合はそこで,それ以外の場合は開始値にbyAngleを加えた値で停止します. toAngleとbyAngleの両方が指定されている場合は,toAngleが優先されます.
RotateTransition rt = new RotateTransition(Duration.millis(3000), rect);
rt.setByAngle(240);
rt.setCycleCount(4);
rt.setAutoReverse(true);
rt.play();
scaleX、scaleYおよびscaleZ変数を一定の間隔で更新することによってノードを拡大縮小します.
値は,(fromX, fromY, fromZ)値が指定されている場合はそこから,それ以外の場合はノードの(scaleX, scaleY, scaleZ)値から 開始され,(toX, toY, toZ)値が指定されている場合はそこで,それ以外の場合は開始値に(byX, byY, byZ)値を加えた値で停止します.(toX, toY, toZ)値と(byX, byY, byZ)値の両方が指定されている場合は,(toX, toY, toZ)値が優先されます.
ScaleTransition st = new ScaleTransition(Duration.millis(3000), circle);
st.setByX(10);
st.setByY(10);;
st.setCycleCount(4);
st.setAutoReverse(true);
st.play();
shapeのstroke変数を一定の間隔で更新することによって図形の線の色を変化させます.
stroke変数は,fromValueが指定されている場合はそこから,それ以外の場合はshapeのstroke値から開始され,toValue値で停止します.
StrokeTransition st = new StrokeTransition(Duration.millis(3000), circle,Color.RED,Color.BLUE);
st.setCycleCount(4);
st.setAutoReverse(true);
st.play();
translateX,translateYおよびtranslateZ変数を一定の間隔で更新することによってノードを平行移動させます.
値は,(fromX, fromY, fromZ)値が指定されている場合はそこから,それ以外の場合はノードの(translateX, translateY, translateZ)値から開始され,(toX, toY, toZ)値が指定されている場合はそこで,それ以外の場合は開始値に(byX, byY, byZ)を加えた値で停止します.(toX, toY, toZ)値と(byX, byY, byZ)値の両方が指定されている場合は,(toX, toY, toZ)値が優先されます.
TranslateTransition tt = new TranslateTransition(Duration.millis(3000),circle);
tt.setFromX(20);
tt.setFromY(20);
tt.setToX(360);
tt.setToY(260);
tt.setInterpolator( Interpolator.LINEAR );
tt.setCycleCount(4);
tt.setAutoReverse(true);
tt.play();
様々なTransitionを連続,もしくは同時に実行するプログラムを作成する.クラス名は「Ex13_1」とする.
時間の推移に応じて,サイズ,場所,色などのプロパティ値を更新することによってアニメーションが出来ます.
具体的には,KeyFrameという初期状態と終了状態,必要ならば複数の中間状態を表すものを,KeyValueという様々なプロパティ値の設定を記述するものを用いてに設定し,KeyFrame間をつなぐ補間方法(Interpolator)を指定してアニメーションを作成します.
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.animation.Timeline;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Interpolator;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.util.Duration;
public class TimeLineExample extends Application {
@Override
public void start(Stage stage) {
stage.setTitle("TimeLine Example");
VBox root = new VBox();
root.setAlignment(Pos.CENTER_LEFT);
root.setPadding(new Insets(10,10,10,10));
root.setSpacing(50.0);
Scene scene = new Scene(root, 500, 300);
scene.setFill(Color.WHITE);
Circle circle = new Circle(100);
circle.setFill(Color.RED);
root.getChildren().addAll(circle);
stage.setScene(scene);
stage.show();
Timeline timeline = new Timeline();
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.setAutoReverse(true);
KeyValue kv = new KeyValue(circle.translateXProperty(), 300, Interpolator.EASE_BOTH);
\\最初の項目は補完する対象を指定:今回はcircleのx座標
\\2つ目は補完の終了値を指定
KeyFrame kf = new KeyFrame(Duration.millis(1000), kv);
timeline.getKeyFrames().add(kf);
timeline.play();
}
public static void main(String[] args) {
launch(args);
}
}
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.Group;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.animation.Timeline;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Interpolator;
import javafx.util.Duration;
public class TimeLineExample2 extends Application {
@Override
public void start(Stage stage) {
stage.setTitle("Fade Transition");
Group root = new Group();
Scene scene = new Scene(root, 500, 300);
scene.setFill(Color.WHITE);
Circle circle = new Circle(20);
circle.setFill(Color.RED);
circle.setCenterX(20);
circle.setCenterY(70);
root.getChildren().addAll(circle);
stage.setScene(scene);
stage.show();
Timeline timeline = new Timeline();
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.setAutoReverse(true);
KeyValue keyValue1 = new KeyValue(circle.translateXProperty(), 480);
MyInterpolator yInterp = new MyInterpolator();
KeyValue keyValue2 = new KeyValue(circle.translateYProperty(), 200, yInterp);
KeyFrame kf1 = new KeyFrame(Duration.millis(2000), keyValue1);
KeyFrame kf2 = new KeyFrame(Duration.millis(2000), keyValue2);
timeline.getKeyFrames().add(kf1);
timeline.getKeyFrames().add(kf2);
timeline.play();
}
public static void main(String[] args) {
launch(args);
}
private class MyInterpolator extends Interpolator {
@Override
protected double curve(double t) {
if( t < 0.5) {
return Math.abs(0.25-t)*3 ;
}else {
return Math.abs(0.75-t)*3 ;
}
}
}
}
TimeLineを使用して,文字列が変化したり移動したりするプログラムを作成する.クラス名は「Ex13_2」とする.
表示される状態の変化の仕方を記述するようにメソッドhandle(long)をオーバーライドします.このhandleメソッドが一定の間隔で呼び出されることでアニメーションが実現されます.
AnimationTimerはstart()によって開始され,stop()で停止されます.
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.animation.AnimationTimer;
public class AnimationTimerExample extends Application {
int STAR_COUNT = 20000;
Rectangle[] rects = new Rectangle[STAR_COUNT]; //四角形を沢山作る
double[] angles = new double[STAR_COUNT]; //四角形それぞれの角度を表す配列変数
long[] start = new long[STAR_COUNT]; //四角形それぞれのスタート時刻を表す配列変数
@Override
public void start(Stage stage) {
//それぞれの配列変数を初期化
for(int i = 0; i < STAR_COUNT; i++) {
rects[i] = new Rectangle(1,1,Color.WHITE);
angles[i] = (double)(2.0*Math.PI*i/STAR_COUNT);
start[i] = i*100000;
}
Group anim = new Group(rects);
stage.setTitle("AnimationTimer Example");
stage.setScene(new Scene(anim, 800, 600, Color.BLACK));
stage.show();
//MyAnimationTimerをmyAnimationTimerとして作成
MyAnimationTimer myAnimationTimer = new MyAnimationTimer(stage);
//myAnimationTimerをスタート
myAnimationTimer.start();
}
//AnimatiomTimerを拡張してMyAnimationTimerを定義
private class MyAnimationTimer extends AnimationTimer {
double width, height, radius;
//MyAnimationTimerのコンストラクター(MyAnimationTimerを作成した時の初期設定)
public MyAnimationTimer(Stage stage) {
width = 0.5 * stage.getWidth();
height = 0.5 * stage.getHeight();
radius = Math.sqrt(2) * Math.max(width, height);
}
//handleメソッドを構成
@Override
public void handle(long now) {
for (int i = 0; i < STAR_COUNT; i++) {
double angle = angles[i]; //中心からの角度
long t = (now - start[i]) % 2000000000; //時刻
double d = t * radius / 2000000000.0; //中心からの距離
rects[i].setTranslateX(Math.cos(angle) * d + width); //四角形のx座標
rects[i].setTranslateY(Math.sin(angle) * d + height); //四角形のy座標
}
}
}
public static void main(String[] args) {
launch(args);
}
}
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.Group;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.event.ActionEvent;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.paint.Color;
import javafx.scene.paint.RadialGradient;
import javafx.animation.AnimationTimer;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.Stop;
public class BallExample extends Application {
Circle ball;
double WIDTH = 600, HEIGHT = 400;
MyAnimationTimer myAnimationTimer;
double vx, vy, vi, theta;
@Override
public void start(Stage stage) {
ball = new Circle(10);
ball.setFill(new RadialGradient(0, .1, -10, -10, 20, false, CycleMethod.NO_CYCLE,
new Stop(0, Color.YELLOW), new Stop(1, Color.RED)));
Group anim = new Group();
Rectangle rect = new Rectangle(0, 0, 600, 400);
rect.setFill(Color.AQUA);
anim.getChildren().add(rect);
anim.getChildren().add(ball);
BorderPane root = new BorderPane();
root.setCenter(anim);
HBox input_angle = new HBox();
TextField tf = new TextField();
tf.setAlignment(Pos.CENTER_RIGHT);
tf.setMaxWidth(50);
tf.setText("30");
Button btn = new Button("Restart");
input_angle.getChildren().addAll(tf, btn);
root.setTop(input_angle);
btn.setOnAction((ActionEvent event) -> {
myAnimationTimer.stop();
String s = tf.getText();
theta = Math.PI*Double.parseDouble(s)/180;
initBall(ball);
myAnimationTimer.start();
});
stage.setTitle("AnimationTimer: Simple bouncing ball");
stage.setScene(new Scene(root, 640, 480));
stage.show();
myAnimationTimer = new MyAnimationTimer(stage);
myAnimationTimer.start();
}
private void initBall(Circle ball) {
ball.setTranslateX( ball.getRadius());
ball.setTranslateY( HEIGHT - ball.getRadius() );
vi = 342;
vx = Math.cos(theta) * vi;
vy = - Math.sin(theta) * vi;
}
public class MyAnimationTimer extends AnimationTimer {
long prev;
public MyAnimationTimer(Stage stage) {
vi = 342;
theta = Math.PI/6;
prev = 0;
ball.setTranslateX( ball.getRadius());
ball.setTranslateY( HEIGHT - ball.getRadius() );
vx = Math.cos(theta) * vi;
vy = - Math.sin(theta) * vi;
}
@Override
public void handle(long now) {
if (prev != 0) {
double td = (double) (now - prev) / 1_000_000_000;
if (ball.getTranslateX() > WIDTH - ball.getRadius()) {
ball.setTranslateX(WIDTH - ball.getRadius());
vx = -vx;
}
if (ball.getTranslateX() < 0 + ball.getRadius()) {
ball.setTranslateX(0 + ball.getRadius());
vx = -vx;
}
if (ball.getTranslateY() > HEIGHT - ball.getRadius()) {
ball.setTranslateY(HEIGHT - ball.getRadius());
vy = -vy;
}
if (ball.getTranslateY() < 0 + ball.getRadius()) {
ball.setTranslateY(0 + ball.getRadius());
vy = -vy;
}
ball.setTranslateX( ball.getTranslateX() + vx * td );
ball.setTranslateY( ball.getTranslateY() + vy * td );
}
prev = now;
}
}
public static void main(String[] args) {
launch(args);
}
}
使用例2を参考にして,初速を入力できるようにし,以下の条件をできるだけ反映したプログラムを作成する.
クラス名は「Ex13_3」とする.