DEV Community

Cover image for Animation
Paul Ngugi
Paul Ngugi

Posted on

Animation

JavaFX provides the Animation class with the core functionality for all animations.

Suppose you want to write a program that animates a rising flag, as shown in Figure below. How do you accomplish the task? There are several ways to program this. An effective one is to use the subclasses of the JavaFX Animation class.

Image description

The abstract Animation class provides the core functionalities for animations in JavaFX, as shown in Figure below. Many concrete subclasses of Animation are provided in JavaFX. This section introduces PathTransition, FadeTransition and Timeline.

Image description

The autoReverse is a Boolean property that indicates whether an animation will reverse its direction on the next cycle. The cycleCount indicates the number of the cycles for the animation. You can use the constant Timeline.INDEFINTE to indicate an indefinite number of cycles. The rate defines the speed of the animation. A negative rate value indicates the opposite direction for the animation. The status is a read-only property that indicates the status of the animation (Animation.Status.PAUSED, Animation.Status.RUNNING, and Animation.Status.STOPPED). The methods pause(), play(), and stop() pauses, plays, and stops an animation.

PathTransition

The PathTransition class animates the the moves of a node along a path from one end to the other over a given time. PathTransition is a subtype of Animation. The UML class diagram for the class is shown in Figure below.

Image description

The Duration class defines a duration of time. It is an immutable class. The class defines constants INDEFINTE, ONE, UNKNOWN, and ZERO to represent an indefinte duration, 1 milliseconds, unknow, and 0 duration. You can use new Duration(double millis) to create an instance of Duration, the add, subtract, multiply, and divide methods to perform arithmetic operations, and the toHours(), toMinutes(), toSeconds(), and toMillis() to return the number of hours, minutes, seconds, and milliseconds in this duration. You can also use compareTo to compare two durations.

The constants NONE and ORTHOGONAL_TO_TANGENT are defined in PathTransition.OrientationType. The latter specifies that the node is kept perpendicular to the path’s tangent along the geometric path.

The program below gives an example that moves a rectangle along the outline of a circle, as shown in Figure below (a).

Image description

package application;
import javafx.animation.PathTransition;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;

public class PathTransitionDemo extends Application {
    @Override // Override the start method in the Application class
    public void start(Stage primaryStage) {
        // Create a pane
        Pane pane = new Pane();

        // Create a rectangle
        Rectangle rectangle = new Rectangle(0, 0, 25, 50);
        rectangle.setFill(Color.ORANGE);

        // Create a circle
        Circle circle = new Circle(125, 100, 50);
        circle.setFill(Color.WHITE);
        circle.setStroke(Color.BLACK);

        // Add circle and rectangle to the pane
        pane.getChildren().add(circle);
        pane.getChildren().add(rectangle);

        // Create a path transition
        PathTransition pt = new PathTransition();
        pt.setDuration(Duration.millis(4000));
        pt.setPath(circle);
        pt.setNode(rectangle);
        pt.setOrientation(PathTransition.OrientationType.ORTHOGONAL_TO_TANGENT);
        pt.setCycleCount(Timeline.INDEFINITE);
        pt.setAutoReverse(true);
        pt.play(); // Start animation

        circle.setOnMousePressed(e -> pt.pause());
        circle.setOnMouseReleased(e -> pt.play());

        // Create a scene and place it in the stage
        Scene scene = new Scene(pane, 250, 200);
        primaryStage.setTitle("PathTransitionDemo"); // Set the stage title
        primaryStage.setScene(scene); // Place the scene in the stage
        primaryStage.show(); // Display the stage
    }

    public static void main(String[] args) {
        Application.launch(args);
    }
}

Enter fullscreen mode Exit fullscreen mode

The program creates a pane (line 17), a rectangle (line 20), and a circle (line 24). The circle and rectangle are placed in the pane (lines 29 and 30). If the circle was not placed in the pane, you will see the screenshot as shown in Figure above (b).

The program creates a path transition (line 33), sets its duration to 4 seconds for one cycle of animation (line 34), sets circle as the path (line 35), sets rectangle as the node (line 36), and sets the orientation to orthogonal to tangent (line 37).

The cycle count is set to indefinite (line 38) so the animation continues forever. The auto reverse is set to true (line 39) so that the direction of the move is reversed in the alternating cycle. The program starts animation by invoking the play() method (line 40).

If the pause() method is replaced by the stop() method in line 42, the animation will start over from the beginning when it restarts.

Below gives the program that animates a flag rising.

package application;
import javafx.animation.PathTransition;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
import javafx.util.Duration;

public class FlagRisingAnimation extends Application {
    @Override // Override the start method in the Application class
    public void start(Stage primaryStage) {
        // Create a pane
        Pane pane = new Pane();

        // Ad an image view and add it to pane
        ImageView imageView = new ImageView("file:/C:/Users/Paul/development/MyJavaFX/src/application/image/ke.jpg");
        pane.getChildren().add(imageView);

        // Create a path transition
        PathTransition pt = new PathTransition(Duration.millis(10000), new Line(100, 200, 100, 0), imageView);
        pt.setCycleCount(5);
        pt.play(); // Start animation

        // Create a scene and place it in the stage
        Scene scene = new Scene(pane, 250, 200);
        primaryStage.setTitle("FlagRisingAnimation"); // Set the stage title
        primaryStage.setScene(scene); // Place the scene in the stage
        primaryStage.show(); // Display the stage
    }

    public static void main(String[] args) {
        Application.launch(args);
    }
}

Enter fullscreen mode Exit fullscreen mode

The program creates a pane (line 15), an image view from an image file (line 18), and places the image view to the page (line 19). A path transition is created with duration of 10 seconds using a line as a path and the image view as the node (lines 22). The image view will move along the line. Since the line is not placed in the scene, you will not see the line in the window.

The cycle count is set to 5 (line 23) so that the animation is repeated five times.

FadeTransition

The FadeTransition class animates the change of the opacity in a node over a given time. FadeTransition is a subtype of Animation. The UML class diagram for the class is shown in Figure below.

Image description

The program below gives an example that applies a fade transition to the filled color in an ellipse, as shown in Figure below.

package application;
import javafx.animation.FadeTransition;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Ellipse;
import javafx.stage.Stage;
import javafx.util.Duration;

public class FadeTransitionDemo extends Application {
    @Override // Override the start method in the Application class
    public void start(Stage primaryStage) {
        // Place an ellipse to the pane
        Pane pane = new Pane();
        Ellipse ellipse = new Ellipse(10, 10, 100, 50);
        ellipse.setFill(Color.RED);
        ellipse.setStroke(Color.BLACK);
        ellipse.centerXProperty().bind(pane.widthProperty().divide(2));
        ellipse.centerYProperty().bind(pane.heightProperty().divide(2));
        ellipse.radiusXProperty().bind(pane.widthProperty().multiply(0.4));
        ellipse.radiusYProperty().bind(pane.heightProperty().multiply(0.4));
        pane.getChildren().add(ellipse);

        // Apply a fade transition to ellipse
        FadeTransition ft = new FadeTransition(Duration.millis(3000), ellipse);
        ft.setFromValue(1.0);
        ft.setToValue(0.1);
        ft.setCycleCount(Timeline.INDEFINITE);
        ft.setAutoReverse(true);
        ft.play(); // Start animation

        // Control animation
        ellipse.setOnMousePressed(e -> ft.pause());
        ellipse.setOnMouseReleased(e -> ft.play());

        // Create a scene and place it in the stage
        Scene scene = new Scene(pane, 200, 150);
        primaryStage.setTitle("FadeTransitionDemo"); // Set the stage title
        primaryStage.setScene(scene); // Place the scene in the stage
        primaryStage.show(); // Display the stage
    }

    public static void main(String[] args) {
        Application.launch(args);
    }
}

Enter fullscreen mode Exit fullscreen mode

Image description

The program creates a pane (line 16) and an ellipse (line 17) and places the ellipse into the pane (line 24). The ellipse’s centerX, centerY, radiusX, and radiusY properties are bound to the pane’s size (lines 20–23).

A fade transition is created with a duration of 3 seconds for the ellipse (line 27). It sets the start opaque to 1.0 (line 28) and the stop opaque 0.1 (line 29). The cycle count is set to infinite so the animation is repeated indefinitely (line 30). When the mouse is pressed, the animation
is paused (line 35). When the mouse is released, the animation resumes from where it was paused (line 36).

Timeline

PathTransition and FadeTransition define specialized animations. The Timeline class can be used to program any animation using one or more KeyFrames. Each KeyFrame is executed sequentially at a specified time interval. Timeline inherits from Animation. You can construct a Timeline using the constructor new Timeline(KeyFrame... keyframes). A KeyFrame can be constructed using

new KeyFrame(Duration duration, EventHandler<ActionEvent> onFinished)

The handler onFinished is called when the duration for the key frame is elapsed.

The program below gives an example that displays a flashing text, as shown in Figure below. The text is on and off alternating to animate flashing.

package application;
import javafx.animation.Animation;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.util.Duration;

public class TimelineDemo extends Application {
    @Override // Override the start method in the Application class
    public void start(Stage primaryStage) {
        StackPane pane = new StackPane();
        Text text = new Text(20, 50, "Programming is fun");
        text.setFill(Color.RED);
        pane.getChildren().add(text); // Place text into the stack pane

        // Create a handler for changing text
        EventHandler<ActionEvent> eventHandler = e -> {
            if(text.getText().length() != 0) {
                text.setText("");
            }
            else {
                text.setText("Programming is fun");
            }
        };

        // Create an animation for alternating text
        Timeline animation = new Timeline(new KeyFrame(Duration.millis(500), eventHandler));
        animation.setCycleCount(Timeline.INDEFINITE);
        animation.play(); // Start animation

        // Pause and resume animation
        text.setOnMouseClicked(e -> {
            if(animation.getStatus() == Animation.Status.PAUSED) {
                animation.play();
            }
            else {
                animation.pause();
            }
        });

        // Create a scene and place it in the stage
        Scene scene = new Scene(pane, 250, 250);
        primaryStage.setTitle("TimelineDemo"); // Set the stage title
        primaryStage.setScene(scene); // Place the scene in the stage
        primaryStage.show(); // Display the stage
    }

    public static void main(String[] args) {
        Application.launch(args);
    }
}

Enter fullscreen mode Exit fullscreen mode

Image description

The program creates a stack pane (line 18) and a text (line 19) and places the text into the pane (line 21). A handler is created to change the text to empty (lines 25–27) if it is not empty or to Progrmming is fun if it is empty (lines 28–30). A KeyFrame is created to run an action event in every half second (line 34). A Timeline animation is created to contain a key frame (lines 34). The animation is set to run indefinitely (line 35).

The mouse clicked event is set for the text (lines 39–46). A mouse click on the text resumes the animation if the animation is paused (lines 40–42), and a mouse click on the text pauses the animation if the animation is running (lines 43–45).

In this post, Case Study: The ClockPane Class, you drew a clock to show the current time. The clock does not tick after it is displayed. What can you do to make the clock display a new current time every second? The key to making the clock tick is to repaint it every second with a new current time. You can use a Timeline to control the repainting of the clock with the code in below. The sample run of the program is shown in Figure below.

package application;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.util.Duration;

public class ClockAnimation extends Application {
    @Override // Override the start method in the Application class
    public void start(Stage primaryStage) {
        ClockPane clock = new ClockPane(); // Create a clock

        // Create a handler for animation
        EventHandler<ActionEvent> eventHandler = e -> {
            clock.setCurrentTime(); // Set a new clock time
        };

        // Create an animation for a running clock
        Timeline animation = new Timeline(new KeyFrame(Duration.millis(1000), eventHandler));
        animation.setCycleCount(Timeline.INDEFINITE);
        animation.play(); // Start animation

        // Create a scene and place it in the stage
        Scene scene = new Scene(clock, 250, 50);
        primaryStage.setTitle("TimelineDemo"); // Set the stage title
        primaryStage.setScene(scene); // Place the scene in the stage
        primaryStage.show(); // Display the stage
    }

    public static void main(String[] args) {
        Application.launch(args);
    }
}

Enter fullscreen mode Exit fullscreen mode

Image description

The program creates an instance clock of ClockPane for displaying a clock (line 14). The ClockPane.java class is defined in this post. The clock is placed in the scene in line 27. An event handler is created for setting the current time in the clock (lines 17–19). This handler is called every second in the key frame in the time line animation (lines 22–24). So the clock time is updated every second in the animation.

Top comments (0)