要說JavaFX跟Swing外觀上最大的改變就是JavaFX多了動畫(Animation)的支援。JavaFX的動畫加入了「關鍵影格(Key Frame)」的概念,因此要同時控制多個不同的元件進行動畫,可以輕易地做到!



關鍵影格

關鍵影格就是在一條影格時間軸上,可以直接被清楚定義出目前狀態的影格。舉個例子「物體0秒的時候是在A的位置,5秒的時候是在B的位置」,這就是關鍵影格的描述,我們可以很清楚的講出哪個物體在什麼時間的狀態是什麼。使用關鍵影格,可以很容易地產生出動畫,用剛才的例子,我們雖然並不清楚物體從開始移動到結束移動的過程,無法直接講出他在1秒的時候是在哪個位置,但是我們可以藉由一些演算公式,來計算該物體在那個時間點的位置,所以當我們從時間軸0秒看到5秒時,物體是從A的位置可以漸漸的移到B的位置。這個原本不存在,由公式計算出來的狀態變化,稱為「補間動畫(Tweening)」。

JavaFX 動畫

在JavaFX中,可以使用TimelineKeyFrame、和KeyValue來製作動畫,如下面的程式:

import java.io.IOException;

import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.effect.BlendMode;
import javafx.scene.effect.BoxBlur;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.StrokeType;
import javafx.stage.Stage;
import javafx.util.Duration;

public class BlurCircleAnimation extends Application {

    //視窗大小
    public static final int width = 800;
    public static final int height = 600;

    //動畫時間
    public static final long duration = 30000;

    //圓形數量
    public static final int count = 30;

    @Override
    public void start(Stage primaryStage) throws IOException {
        //以root這個Group作為Scene的root node
        Group root = new Group();
        Scene scene = new Scene(root, width, height, Color.BLACK);
        primaryStage.setScene(scene);

        //建立顏色矩形colors,大小先不設定,顏色為從左下到右上的漸層
        Rectangle colors = new Rectangle(0, 0,
                new LinearGradient(0f, 1f, 1f, 0f, true, CycleMethod.NO_CYCLE, new Stop[]{
            new Stop(0, Color.web("#f8bd55")),
            new Stop(0.14, Color.web("#c0fe56")),
            new Stop(0.28, Color.web("#5dfbc1")),
            new Stop(0.43, Color.web("#64c2f8")),
            new Stop(0.57, Color.web("#be4af7")),
            new Stop(0.71, Color.web("#ed5fc2")),
            new Stop(0.85, Color.web("#ef504c")),
            new Stop(1, Color.web("#f2660f")),}));
        //讓colors大小隨著Scene的長寬做變化(如果有先設定colors的大小,那麼它將只有在放大的時候才會延展)
        colors.widthProperty().bind(scene.widthProperty());
        colors.heightProperty().bind(scene.heightProperty());

        //繪製圓形
        Group circles = new Group();
        for (int i = 0; i < count; i++) {
            Circle circle = new Circle(Math.random() * 50 + 75, Color.web("white", 0.15)); //建立圓形,半徑為75~125,顏色為白色,不透明度15%
            circle.setStrokeType(StrokeType.OUTSIDE); //設定邊框類型
//            circle.setStroke(Color.web("white", 0.2)); //設定邊框顏色為白色,不透明度20%
            circle.setStrokeWidth(4); //設定邊框粗細
            circles.setEffect(new BoxBlur(10, 10, 3)); //設定模糊
            circles.getChildren().add(circle); //將新建立的圓形加入群組
        }

        //製作動畫
        Timeline timeline = new Timeline(); //動畫時間軸
        circles.getChildren().forEach((circle) -> { //將所有的圓形都設動畫
            //製作起始、終止的關鍵影格
            KeyFrame start = new KeyFrame(Duration.ZERO, new KeyValue(circle.translateXProperty(), Math.random() * scene.getWidth()), new KeyValue(circle.translateYProperty(), Math.random() * scene.getHeight()));
            KeyFrame end = new KeyFrame(new Duration(duration), new KeyValue(circle.translateXProperty(), Math.random() * scene.getWidth()), new KeyValue(circle.translateYProperty(), Math.random() * scene.getHeight()));

            //加入關鍵影格
            timeline.getKeyFrames().addAll(start, end);
        });
        timeline.play(); //播放時間軸動畫

        //***混合模式,以下幾種都可以玩玩看
//        colors.setBlendMode(BlendMode.SCREEN);
        colors.setBlendMode(BlendMode.OVERLAY);

        //加入Node。注意這裡的加入順序! 
        root.getChildren().add(circles);
        root.getChildren().add(colors);

        primaryStage.show();
    }
}

執行結果:

Screen混合模式

Overlay混合模式