Animation
Tags: java, programming, learning, beginners
Published: 2024-06-24 22:20:30

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h31e71gm3xfhry3cm2hw.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1154aeibyyh9q1v07yct.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a20l70xpq4gy1u6b8oq1.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r23l7q66zssopcbkj1bi.png)

```
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);
	}
}

```

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);
	}
}

```

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xohaf6syuqxu23if2p5d.png)

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);
	}
}

```


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/caya17q4zzegar3lbfdl.png)

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);
	}
}

```


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i29y9idqw0c1tiqxnm5i.png)

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](https://dev.to/paulike/case-study-the-clockpane-class-4bpg), 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);
	}
}

```


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i0zxppif219idoz7kzgq.png)

The program creates an instance **clock** of **ClockPane** for displaying a clock (line 14). The **ClockPane.java** class is defined in [this post](https://dev.to/paulike/case-study-the-clockpane-class-4bpg). 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.