Java JavaFX Animation
Animation brings user interfaces to life, creating engaging and interactive experiences. In this tutorial, we'll explore JavaFX's powerful animation capabilities that allow you to create smooth, professional-looking animations with minimal code.
Introduction to JavaFX Animation
JavaFX provides a robust animation framework that allows developers to create various types of animations, from simple transitions to complex animated sequences. Animations in JavaFX are primarily handled through two main approaches:
- Timeline Animations: Low-level animations that give fine-grained control over property changes over time.
- Transitions: High-level, pre-built animations for common effects like fading, rotation, and scaling.
Both approaches are based on the fundamental concept of manipulating object properties over a specified duration to create the illusion of smooth movement or transformation.
Setting Up JavaFX Animation
Before diving into creating animations, make sure your project is set up correctly with JavaFX:
import javafx.animation.*;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
Now, let's start by creating a simple application that we'll use to demonstrate animations:
public class AnimationDemo extends Application {
@Override
public void start(Stage primaryStage) {
// Create a container for our animated objects
Pane root = new Pane();
// Create a scene
Scene scene = new Scene(root, 600, 400);
// Set up the stage
primaryStage.setTitle("JavaFX Animation Demo");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Timeline Animation
Timeline animations provide the most flexibility and control. They operate by defining "keyframes" - points in time where properties have specific values.
Basic Timeline Example
Let's create a simple animation that moves a rectangle across the screen:
// Create a rectangle to animate
Rectangle rect = new Rectangle(100, 50, 100, 50);
rect.setFill(javafx.scene.paint.Color.BLUE);
root.getChildren().add(rect);
// Create a timeline animation
Timeline timeline = new Timeline();
timeline.setCycleCount(Timeline.INDEFINITE); // Loop indefinitely
timeline.setAutoReverse(true); // Reverse direction at the end
// Define the keyframes (start and end points of animation)
KeyFrame start = new KeyFrame(
Duration.ZERO,
new KeyValue(rect.translateXProperty(), 0)
);
KeyFrame end = new KeyFrame(
Duration.seconds(2),
new KeyValue(rect.translateXProperty(), 400)
);
// Add the keyframes to the timeline
timeline.getKeyFrames().addAll(start, end);
// Start the animation
timeline.play();
In this example:
- We create a blue rectangle
- We set up a Timeline that will run indefinitely and reverse direction
- We define two KeyFrames - one at the start (0 seconds) and one at the end (2 seconds)
- Each KeyFrame contains a KeyValue that specifies what property value should be at that time
- Finally, we start the animation with
play()
Timeline Control Methods
Timelines provide several useful control methods:
// Pausing an animation
timeline.pause();
// Stopping an animation (resets to start)
timeline.stop();
// Getting the current rate (speed)
double rate = timeline.getRate();
// Setting the rate (2.0 = 2x speed, 0.5 = half speed)
timeline.setRate(2.0);
Transition Animations
JavaFX provides pre-built transitions for common animation effects. These are easier to use than timelines for basic animations.
Common Transitions
Here are some of the most commonly used transitions:
FadeTransition
Animates the opacity of a node:
Rectangle rect = new Rectangle(250, 150, 100, 80);
rect.setFill(javafx.scene.paint.Color.RED);
root.getChildren().add(rect);
// Create a fade transition
FadeTransition fadeTransition = new FadeTransition(Duration.seconds(2), rect);
fadeTransition.setFromValue(1.0); // Start fully opaque
fadeTransition.setToValue(0.1); // Fade to nearly transparent
fadeTransition.setCycleCount(Transition.INDEFINITE);
fadeTransition.setAutoReverse(true);
fadeTransition.play();
TranslateTransition
Moves a node from one position to another:
Rectangle rect = new Rectangle(50, 50, 80, 80);
rect.setFill(javafx.scene.paint.Color.GREEN);
root.getChildren().add(rect);
// Create a translate transition
TranslateTransition translateTransition = new TranslateTransition(Duration.seconds(3), rect);
translateTransition.setFromX(0);
translateTransition.setToX(300);
translateTransition.setFromY(0);
translateTransition.setToY(200);
translateTransition.setCycleCount(Transition.INDEFINITE);
translateTransition.setAutoReverse(true);
translateTransition.play();
RotateTransition
Rotates a node around its center:
Rectangle rect = new Rectangle(250, 150, 100, 80);
rect.setFill(javafx.scene.paint.Color.ORANGE);
root.getChildren().add(rect);
// Create a rotate transition
RotateTransition rotateTransition = new RotateTransition(Duration.seconds(4), rect);
rotateTransition.setByAngle(360); // Rotate a full 360 degrees
rotateTransition.setCycleCount(Transition.INDEFINITE);
rotateTransition.play();
ScaleTransition
Changes the size of a node:
Rectangle rect = new Rectangle(250, 150, 100, 80);
rect.setFill(javafx.scene.paint.Color.PURPLE);
root.getChildren().add(rect);
// Create a scale transition
ScaleTransition scaleTransition = new ScaleTransition(Duration.seconds(2), rect);
scaleTransition.setFromX(1.0);
scaleTransition.setFromY(1.0);
scaleTransition.setToX(2.0); // Double the width
scaleTransition.setToY(0.5); // Half the height
scaleTransition.setCycleCount(Transition.INDEFINITE);
scaleTransition.setAutoReverse(true);
scaleTransition.play();
Combining Animations
Sequential Animations
To run animations one after another, use SequentialTransition
:
Rectangle rect = new Rectangle(50, 50, 100, 50);
rect.setFill(javafx.scene.paint.Color.BLUE);
root.getChildren().add(rect);
// Create individual transitions
FadeTransition fade = new FadeTransition(Duration.seconds(1), rect);
fade.setFromValue(1.0);
fade.setToValue(0.3);
RotateTransition rotate = new RotateTransition(Duration.seconds(2), rect);
rotate.setByAngle(360);
TranslateTransition translate = new TranslateTransition(Duration.seconds(1), rect);
translate.setToX(200);
// Combine them in sequence
SequentialTransition sequentialTransition = new SequentialTransition();
sequentialTransition.getChildren().addAll(fade, rotate, translate);
sequentialTransition.setCycleCount(Transition.INDEFINITE);
sequentialTransition.setAutoReverse(true);
sequentialTransition.play();
Parallel Animations
To run animations simultaneously, use ParallelTransition
:
Rectangle rect = new Rectangle(150, 150, 100, 100);
rect.setFill(javafx.scene.paint.Color.CRIMSON);
root.getChildren().add(rect);
// Create individual transitions
FadeTransition fade = new FadeTransition(Duration.seconds(2), rect);
fade.setFromValue(1.0);
fade.setToValue(0.3);
RotateTransition rotate = new RotateTransition(Duration.seconds(2), rect);
rotate.setByAngle(180);
// Run them in parallel
ParallelTransition parallelTransition = new ParallelTransition();
parallelTransition.getChildren().addAll(fade, rotate);
parallelTransition.setCycleCount(Transition.INDEFINITE);
parallelTransition.setAutoReverse(true);
parallelTransition.play();
Animation Events
You can add event handlers to respond to animation state changes:
FadeTransition fade = new FadeTransition(Duration.seconds(2), rect);
fade.setFromValue(1.0);
fade.setToValue(0.1);
fade.setCycleCount(4);
// Event handlers
fade.setOnFinished(event -> System.out.println("Animation finished!"));
fade.statusProperty().addListener((obs, oldStatus, newStatus) -> {
System.out.println("Animation status changed: " + newStatus);
});
fade.play();
Practical Example: Button Animation
Let's create a practical example - an animated button that gives visual feedback when clicked:
import javafx.animation.*;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
public class AnimatedButtonExample extends Application {
@Override
public void start(Stage primaryStage) {
Button button = new Button("Click Me!");
button.setPrefSize(150, 50);
// Set up animation
ScaleTransition scaleDown = new ScaleTransition(Duration.millis(100), button);
scaleDown.setToX(0.9);
scaleDown.setToY(0.9);
ScaleTransition scaleUp = new ScaleTransition(Duration.millis(100), button);
scaleUp.setToX(1.0);
scaleUp.setToY(1.0);
SequentialTransition clickAnimation = new SequentialTransition(scaleDown, scaleUp);
// Trigger animation on click
button.setOnAction(event -> {
clickAnimation.play();
});
VBox root = new VBox(20);
root.setPadding(new Insets(20));
root.getChildren().add(button);
Scene scene = new Scene(root, 300, 200);
primaryStage.setTitle("Animated Button");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
In this example, when the button is clicked:
- It first scales down to 90% of its size
- Then it scales back up to its original size
- This creates a "push" effect that gives the user visual feedback
Animation Lifecycle
The following diagram illustrates the typical lifecycle of a JavaFX animation:
Advanced Example: Creating an Interactive Game Element
Let's create a simple "dodge the falling objects" game element using JavaFX animations:
import javafx.animation.*;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.util.Random;
public class FallingObjectsGame extends Application {
private static final int WIDTH = 600;
private static final int HEIGHT = 400;
private Rectangle player;
private Pane gamePane;
private Timeline gameLoop;
private Random random = new Random();
@Override
public void start(Stage primaryStage) {
gamePane = new Pane();
Scene scene = new Scene(gamePane, WIDTH, HEIGHT);
// Create player rectangle
player = new Rectangle(WIDTH/2, HEIGHT-30, 50, 15);
player.setFill(Color.BLUE);
gamePane.getChildren().add(player);
// Set up keyboard input
scene.setOnKeyPressed(e -> {
if (e.getCode() == KeyCode.LEFT && player.getX() > 0) {
player.setX(player.getX() - 15);
} else if (e.getCode() == KeyCode.RIGHT && player.getX() < WIDTH - player.getWidth()) {
player.setX(player.getX() + 15);
}
});
// Create game loop
gameLoop = new Timeline(new KeyFrame(Duration.millis(50), e -> {
// Create new falling object occasionally
if (random.nextInt(20) < 2) {
createFallingObject();
}
}));
gameLoop.setCycleCount(Timeline.INDEFINITE);
gameLoop.play();
primaryStage.setTitle("Dodge Game");
primaryStage.setScene(scene);
primaryStage.show();
}
private void createFallingObject() {
// Create a circle at random X position at the top of the screen
Circle circle = new Circle(10, Color.RED);
circle.setCenterX(random.nextInt(WIDTH - 20) + 10);
circle.setCenterY(0);
gamePane.getChildren().add(circle);
// Create falling animation
TranslateTransition fall = new TranslateTransition(Duration.seconds(3), circle);
fall.setToY(HEIGHT);
// When the animation finishes, remove the circle
fall.setOnFinished(e -> gamePane.getChildren().remove(circle));
fall.play();
}
public static void main(String[] args) {
launch(args);
}
}
In this example:
- We create a blue player rectangle controlled by the left and right arrow keys
- A game loop (Timeline) periodically spawns red circles at random positions at the top
- The circles fall down the screen using TranslateTransition
- When the animation finishes, the circles are removed from the scene
This demonstrates how JavaFX animations can be used in interactive applications like games.
Summary
JavaFX provides a powerful animation framework that makes it easy to create interactive and visually appealing user interfaces. In this tutorial, we covered:
- Timeline animations for fine-grained control over property changes
- Transition animations for common effects like fade, rotate, scale, and translate
- Combining animations with sequential and parallel transitions
- Animation events to respond to animation state changes
- Practical examples including button feedback and a simple game element
With these tools, you can bring your JavaFX applications to life and create more engaging user experiences.
Exercises
- Create a "pulsing" button that grows and shrinks continuously until clicked.
- Implement a loading spinner using RotateTransition.
- Create a notification that slides in from the side of the screen, stays visible for a few seconds, then slides out.
- Build a simple slideshow that transitions between images with fade effects.
- Extend the "dodge the falling objects" game to detect collisions and keep score.
Additional Resources
- JavaFX Animation API Documentation
- Oracle JavaFX Animation Tutorial
- OpenJFX Project
- Book: "Pro JavaFX 9" by Johan Vos, Stephen Chin, and James Weaver
Happy animating!
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)