google-site-verification: google61fe8ba583a51912.html
JavaFX Animation Timeline: Complete Guide

JavaFX provides a powerful animation framework with the Timeline class being one of the most versatile tools for creating complex animations. This guide covers everything from basic animations to advanced timeline techniques.

Basic Timeline Concepts

Example 1: Simple Property Animation

import javafx.animation.*; 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.stage.Stage; import javafx.util.Duration; public class BasicTimelineDemo extends Application { @Override public void start(Stage primaryStage) { Pane root = new Pane(); Scene scene = new Scene(root, 800, 600); // Create a rectangle to animate Rectangle rect = new Rectangle(50, 50, 100, 100); rect.setFill(Color.BLUE); root.getChildren().add(rect); // Create a basic timeline for movement Timeline timeline = new Timeline(); // KeyFrames define property values at specific times KeyFrame startFrame = new KeyFrame(Duration.ZERO, new KeyValue(rect.xProperty(), 50), new KeyValue(rect.yProperty(), 50), new KeyValue(rect.fillProperty(), Color.BLUE) ); KeyFrame middleFrame = new KeyFrame(Duration.seconds(1), new KeyValue(rect.xProperty(), 300), new KeyValue(rect.yProperty(), 200), new KeyValue(rect.fillProperty(), Color.RED) ); KeyFrame endFrame = new KeyFrame(Duration.seconds(2), new KeyValue(rect.xProperty(), 600), new KeyValue(rect.yProperty(), 400), new KeyValue(rect.fillProperty(), Color.GREEN) ); timeline.getKeyFrames().addAll(startFrame, middleFrame, endFrame); timeline.setCycleCount(Timeline.INDEFINITE); timeline.setAutoReverse(true); // Start the animation timeline.play(); primaryStage.setTitle("Basic Timeline Animation"); primaryStage.setScene(scene); primaryStage.show(); } public static void main(String[] args) { launch(args); } }

Example 2: Multiple Animations with Interpolators

import javafx.animation.*; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; import javafx.scene.shape.Circle; import javafx.stage.Stage; import javafx.util.Duration; public class InterpolatorDemo extends Application { @Override public void start(Stage primaryStage) { Pane root = new Pane(); Scene scene = new Scene(root, 800, 600); // Create multiple circles with different interpolators Circle circle1 = createCircle(100, 100, Color.RED); Circle circle2 = createCircle(100, 200, Color.BLUE); Circle circle3 = createCircle(100, 300, Color.GREEN); Circle circle4 = createCircle(100, 400, Color.ORANGE); root.getChildren().addAll(circle1, circle2, circle3, circle4); // Different interpolators for different effects createAnimation(circle1, Interpolator.LINEAR, "Linear"); createAnimation(circle2, Interpolator.EASE_BOTH, "Ease Both"); createAnimation(circle3, Interpolator.EASE_IN, "Ease In"); createAnimation(circle4, Interpolator.EASE_OUT, "Ease Out"); primaryStage.setTitle("Interpolator Comparison"); primaryStage.setScene(scene); primaryStage.show(); } private Circle createCircle(double x, double y, Color color) { Circle circle = new Circle(x, y, 30, color); circle.setStroke(Color.BLACK); circle.setStrokeWidth(2); return circle; } private void createAnimation(Circle circle, Interpolator interpolator, String label) { Timeline timeline = new Timeline(); KeyValue startX = new KeyValue(circle.centerXProperty(), 100, interpolator); KeyValue endX = new KeyValue(circle.centerXProperty(), 700, interpolator); KeyFrame start = new KeyFrame(Duration.ZERO, startX); KeyFrame end = new KeyFrame(Duration.seconds(3), endX); timeline.getKeyFrames().addAll(start, end); timeline.setCycleCount(Timeline.INDEFINITE); timeline.setAutoReverse(true); timeline.play(); } public static void main(String[] args) { launch(args); } }

Advanced Timeline Techniques

Example 3: Complex Multi-Property Animation

import javafx.animation.*; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.layout.Pane; import javafx.scene.paint.*; import javafx.scene.shape.Rectangle; import javafx.stage.Stage; import javafx.util.Duration; public class ComplexAnimationDemo extends Application { @Override public void start(Stage primaryStage) { Pane root = new Pane(); Scene scene = new Scene(root, 800, 600); Rectangle rect = new Rectangle(100, 100, 200, 200); // Create a gradient fill Stop[] stops = new Stop[] { new Stop(0, Color.RED), new Stop(1, Color.BLUE) }; LinearGradient gradient = new LinearGradient(0, 0, 1, 1, true, CycleMethod.NO_CYCLE, stops); rect.setFill(gradient); rect.setStroke(Color.BLACK); rect.setStrokeWidth(3); root.getChildren().add(rect); // Complex timeline with multiple properties Timeline timeline = new Timeline(); // Frame 0: Initial state KeyFrame frame0 = new KeyFrame(Duration.ZERO, new KeyValue(rect.xProperty(), 100), new KeyValue(rect.yProperty(), 100), new KeyValue(rect.widthProperty(), 200), new KeyValue(rect.heightProperty(), 200), new KeyValue(rect.rotateProperty(), 0), new KeyValue(rect.opacityProperty(), 1.0), new KeyValue(rect.scaleXProperty(), 1.0), new KeyValue(rect.scaleYProperty(), 1.0) ); // Frame 1: Scale and rotate KeyFrame frame1 = new KeyFrame(Duration.seconds(1), new KeyValue(rect.xProperty(), 200), new KeyValue(rect.yProperty(), 150), new KeyValue(rect.widthProperty(), 150), new KeyValue(rect.heightProperty(), 150), new KeyValue(rect.rotateProperty(), 45), new KeyValue(rect.opacityProperty(), 0.8), new KeyValue(rect.scaleXProperty(), 1.2), new KeyValue(rect.scaleYProperty(), 1.2) ); // Frame 2: Different transformation KeyFrame frame2 = new KeyFrame(Duration.seconds(2), new KeyValue(rect.xProperty(), 400), new KeyValue(rect.yProperty(), 300), new KeyValue(rect.widthProperty(), 100), new KeyValue(rect.heightProperty(), 100), new KeyValue(rect.rotateProperty(), 90), new KeyValue(rect.opacityProperty(), 0.6), new KeyValue(rect.scaleXProperty(), 0.8), new KeyValue(rect.scaleYProperty(), 0.8) ); // Frame 3: Return to similar to original KeyFrame frame3 = new KeyFrame(Duration.seconds(3), new KeyValue(rect.xProperty(), 500), new KeyValue(rect.yProperty(), 400), new KeyValue(rect.widthProperty(), 200), new KeyValue(rect.heightProperty(), 200), new KeyValue(rect.rotateProperty(), 0), new KeyValue(rect.opacityProperty(), 1.0), new KeyValue(rect.scaleXProperty(), 1.0), new KeyValue(rect.scaleYProperty(), 1.0) ); timeline.getKeyFrames().addAll(frame0, frame1, frame2, frame3); timeline.setCycleCount(Timeline.INDEFINITE); timeline.setAutoReverse(true); timeline.play(); primaryStage.setTitle("Complex Multi-Property Animation"); primaryStage.setScene(scene); primaryStage.show(); } public static void main(String[] args) { launch(args); } }

Example 4: Timeline Events and Controls

import javafx.animation.*; import javafx.application.Application; import javafx.geometry.Insets; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.layout.*; import javafx.scene.paint.Color; import javafx.scene.shape.Circle; import javafx.stage.Stage; import javafx.util.Duration; public class TimelineControlsDemo extends Application { private Timeline timeline; private Label statusLabel; private Circle ball; @Override public void start(Stage primaryStage) { VBox root = new VBox(20); root.setPadding(new Insets(20)); Scene scene = new Scene(root, 800, 600); // Create control panel HBox controls = createControlPanel(); // Create status label statusLabel = new Label("Status: Stopped"); // Create animation area Pane animationPane = new Pane(); animationPane.setPrefSize(760, 400); animationPane.setStyle("-fx-border-color: black; -fx-border-width: 1px;"); // Create animated ball ball = new Circle(50, 200, 30, Color.BLUE); animationPane.getChildren().add(ball); // Create timeline createTimeline(); root.getChildren().addAll(controls, statusLabel, animationPane); primaryStage.setTitle("Timeline Controls Demo"); primaryStage.setScene(scene); primaryStage.show(); } private HBox createControlPanel() { HBox controls = new HBox(10); Button playBtn = new Button("Play"); Button pauseBtn = new Button("Pause"); Button stopBtn = new Button("Stop"); Button reverseBtn = new Button("Reverse"); Button speedUpBtn = new Button("Speed Up"); Button slowDownBtn = new Button("Slow Down"); playBtn.setOnAction(e -> { timeline.play(); statusLabel.setText("Status: Playing"); }); pauseBtn.setOnAction(e -> { timeline.pause(); statusLabel.setText("Status: Paused"); }); stopBtn.setOnAction(e -> { timeline.stop(); statusLabel.setText("Status: Stopped"); }); reverseBtn.setOnAction(e -> { timeline.setRate(-timeline.getRate()); statusLabel.setText("Status: " + (timeline.getRate() > 0 ? "Forward" : "Reverse")); }); speedUpBtn.setOnAction(e -> { timeline.setRate(timeline.getRate() * 1.5); statusLabel.setText(String.format("Status: Playing (Rate: %.1fx)", timeline.getRate())); }); slowDownBtn.setOnAction(e -> { timeline.setRate(timeline.getRate() / 1.5); statusLabel.setText(String.format("Status: Playing (Rate: %.1fx)", timeline.getRate())); }); controls.getChildren().addAll(playBtn, pauseBtn, stopBtn, reverseBtn, speedUpBtn, slowDownBtn); return controls; } private void createTimeline() { timeline = new Timeline(); // Create a bouncing ball animation KeyFrame start = new KeyFrame(Duration.ZERO, new KeyValue(ball.centerXProperty(), 50), new KeyValue(ball.centerYProperty(), 200), new KeyValue(ball.fillProperty(), Color.BLUE) ); KeyFrame middle = new KeyFrame(Duration.seconds(1), new KeyValue(ball.centerXProperty(), 400), new KeyValue(ball.centerYProperty(), 50), new KeyValue(ball.fillProperty(), Color.RED) ); KeyFrame end = new KeyFrame(Duration.seconds(2), new KeyValue(ball.centerXProperty(), 750), new KeyValue(ball.centerYProperty(), 200), new KeyValue(ball.fillProperty(), Color.GREEN) ); timeline.getKeyFrames().addAll(start, middle, end); timeline.setCycleCount(Timeline.INDEFINITE); timeline.setAutoReverse(true); // Add event handlers timeline.setOnFinished(e -> { statusLabel.setText("Status: Finished Cycle"); }); } public static void main(String[] args) { launch(args); } }

Timeline with Custom Interpolators

Example 5: Custom Interpolation and KeyFrames

import javafx.animation.*; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; import javafx.scene.shape.Circle; import javafx.stage.Stage; import javafx.util.Duration; public class CustomInterpolatorDemo extends Application { @Override public void start(Stage primaryStage) { Pane root = new Pane(); Scene scene = new Scene(root, 800, 600); // Create circles with different custom interpolators Circle circle1 = createCircle(100, 100, Color.RED); Circle circle2 = createCircle(100, 200, Color.BLUE); Circle circle3 = createCircle(100, 300, Color.GREEN); root.getChildren().addAll(circle1, circle2, circle3); // Custom interpolators createBounceAnimation(circle1); createElasticAnimation(circle2); createCustomEaseAnimation(circle3); primaryStage.setTitle("Custom Interpolator Demo"); primaryStage.setScene(scene); primaryStage.show(); } private Circle createCircle(double x, double y, Color color) { Circle circle = new Circle(x, y, 20, color); circle.setStroke(Color.BLACK); return circle; } private void createBounceAnimation(Circle circle) { Interpolator bounceInterpolator = new Interpolator() { @Override protected double curve(double t) { // Bounce effect if (t < 1/2.75) { return 7.5625 * t * t; } else if (t < 2/2.75) { return 7.5625 * (t -= 1.5/2.75) * t + 0.75; } else if (t < 2.5/2.75) { return 7.5625 * (t -= 2.25/2.75) * t + 0.9375; } else { return 7.5625 * (t -= 2.625/2.75) * t + 0.984375; } } }; Timeline timeline = new Timeline(); KeyValue startX = new KeyValue(circle.centerXProperty(), 100, bounceInterpolator); KeyValue endX = new KeyValue(circle.centerXProperty(), 700, bounceInterpolator); timeline.getKeyFrames().addAll( new KeyFrame(Duration.ZERO, startX), new KeyFrame(Duration.seconds(2), endX) ); timeline.setCycleCount(Timeline.INDEFINITE); timeline.setAutoReverse(true); timeline.play(); } private void createElasticAnimation(Circle circle) { Interpolator elasticInterpolator = new Interpolator() { @Override protected double curve(double t) { // Elastic effect return Math.sin(13 * Math.PI / 2 * t) * Math.pow(2, 10 * (t - 1)); } }; Timeline timeline = new Timeline(); KeyValue startX = new KeyValue(circle.centerXProperty(), 100, elasticInterpolator); KeyValue endX = new KeyValue(circle.centerXProperty(), 700, elasticInterpolator); timeline.getKeyFrames().addAll( new KeyFrame(Duration.ZERO, startX), new KeyFrame(Duration.seconds(2), endX) ); timeline.setCycleCount(Timeline.INDEFINITE); timeline.setAutoReverse(true); timeline.play(); } private void createCustomEaseAnimation(Circle circle) { Interpolator customInterpolator = new Interpolator() { @Override protected double curve(double t) { // Custom ease-in-out with overshoot if (t < 0.5) { return 2 * t * t; } else { return -2 * t * t + 4 * t - 1; } } }; Timeline timeline = new Timeline(); KeyValue startX = new KeyValue(circle.centerXProperty(), 100, customInterpolator); KeyValue endX = new KeyValue(circle.centerXProperty(), 700, customInterpolator); timeline.getKeyFrames().addAll( new KeyFrame(Duration.ZERO, startX), new KeyFrame(Duration.seconds(2), endX) ); timeline.setCycleCount(Timeline.INDEFINITE); timeline.setAutoReverse(true); timeline.play(); } public static void main(String[] args) { launch(args); } }

Sequential and Parallel Timelines

Example 6: Complex Timeline Sequencing

import javafx.animation.*; 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.stage.Stage; import javafx.util.Duration; public class SequentialTimelineDemo extends Application { @Override public void start(Stage primaryStage) { Pane root = new Pane(); Scene scene = new Scene(root, 800, 600); Rectangle rect1 = new Rectangle(50, 50, 80, 80); rect1.setFill(Color.RED); Rectangle rect2 = new Rectangle(50, 150, 80, 80); rect2.setFill(Color.BLUE); Rectangle rect3 = new Rectangle(50, 250, 80, 80); rect3.setFill(Color.GREEN); root.getChildren().addAll(rect1, rect2, rect3); // Create individual timelines Timeline timeline1 = createMoveTimeline(rect1, Color.RED, Color.ORANGE); Timeline timeline2 = createScaleTimeline(rect2, Color.BLUE, Color.PURPLE); Timeline timeline3 = createRotateTimeline(rect3, Color.GREEN, Color.YELLOW); // Sequential animation - one after another SequentialTransition sequential = new SequentialTransition( timeline1, timeline2, timeline3 ); sequential.setCycleCount(Timeline.INDEFINITE); // Parallel animation - all at once ParallelTransition parallel = new ParallelTransition( createMoveTimeline(rect1, Color.RED, Color.ORANGE), createScaleTimeline(rect2, Color.BLUE, Color.PURPLE), createRotateTimeline(rect3, Color.GREEN, Color.YELLOW) ); parallel.setCycleCount(Timeline.INDEFINITE); parallel.setAutoReverse(true); // Start sequential first, then switch to parallel sequential.play(); // Switch to parallel after sequential completes one cycle sequential.setOnFinished(e -> { sequential.stop(); parallel.play(); }); primaryStage.setTitle("Sequential and Parallel Timelines"); primaryStage.setScene(scene); primaryStage.show(); } private Timeline createMoveTimeline(Rectangle rect, Color startColor, Color endColor) { Timeline timeline = new Timeline(); KeyFrame start = new KeyFrame(Duration.ZERO, new KeyValue(rect.xProperty(), 50), new KeyValue(rect.fillProperty(), startColor) ); KeyFrame end = new KeyFrame(Duration.seconds(2), new KeyValue(rect.xProperty(), 600), new KeyValue(rect.fillProperty(), endColor) ); timeline.getKeyFrames().addAll(start, end); timeline.setAutoReverse(true); return timeline; } private Timeline createScaleTimeline(Rectangle rect, Color startColor, Color endColor) { Timeline timeline = new Timeline(); KeyFrame start = new KeyFrame(Duration.ZERO, new KeyValue(rect.scaleXProperty(), 1.0), new KeyValue(rect.scaleYProperty(), 1.0), new KeyValue(rect.fillProperty(), startColor) ); KeyFrame end = new KeyFrame(Duration.seconds(2), new KeyValue(rect.scaleXProperty(), 1.5), new KeyValue(rect.scaleYProperty(), 1.5), new KeyValue(rect.fillProperty(), endColor) ); timeline.getKeyFrames().addAll(start, end); timeline.setAutoReverse(true); return timeline; } private Timeline createRotateTimeline(Rectangle rect, Color startColor, Color endColor) { Timeline timeline = new Timeline(); KeyFrame start = new KeyFrame(Duration.ZERO, new KeyValue(rect.rotateProperty(), 0), new KeyValue(rect.fillProperty(), startColor) ); KeyFrame end = new KeyFrame(Duration.seconds(2), new KeyValue(rect.rotateProperty(), 360), new KeyValue(rect.fillProperty(), endColor) ); timeline.getKeyFrames().addAll(start, end); timeline.setAutoReverse(true); return timeline; } public static void main(String[] args) { launch(args); } }

Timeline with Dynamic Properties

Example 7: Animating Based on User Input

import javafx.animation.*; import javafx.application.Application; import javafx.geometry.Insets; import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.layout.*; import javafx.scene.paint.Color; import javafx.scene.shape.Circle; import javafx.stage.Stage; import javafx.util.Duration; public class DynamicTimelineDemo extends Application { private Timeline timeline; private Circle ball; private Slider durationSlider; private Slider cycleCountSlider; private CheckBox autoReverseCheck; private ComboBox<String> interpolatorCombo; @Override public void start(Stage primaryStage) { VBox root = new VBox(20); root.setPadding(new Insets(20)); Scene scene = new Scene(root, 800, 600); // Create control panel GridPane controls = createControlPanel(); // Create animation area Pane animationPane = new Pane(); animationPane.setPrefSize(760, 400); animationPane.setStyle("-fx-border-color: black; -fx-border-width: 1px;"); // Create animated ball ball = new Circle(100, 200, 30, Color.BLUE); animationPane.getChildren().add(ball); // Create initial timeline createTimeline(); root.getChildren().addAll(controls, animationPane); primaryStage.setTitle("Dynamic Timeline Controls"); primaryStage.setScene(scene); primaryStage.show(); } private GridPane createControlPanel() { GridPane grid = new GridPane(); grid.setHgap(10); grid.setVgap(10); grid.setPadding(new Insets(10)); // Duration slider durationSlider = new Slider(0.5, 5.0, 2.0); durationSlider.setShowTickLabels(true); durationSlider.setShowTickMarks(true); durationSlider.setMajorTickUnit(0.5); // Cycle count slider cycleCountSlider = new Slider(1, 10, 3); cycleCountSlider.setShowTickLabels(true); cycleCountSlider.setShowTickMarks(true); cycleCountSlider.setMajorTickUnit(1); cycleCountSlider.setSnapToTicks(true); // Auto-reverse checkbox autoReverseCheck = new CheckBox("Auto Reverse"); autoReverseCheck.setSelected(true); // Interpolator combo box interpolatorCombo = new ComboBox<>(); interpolatorCombo.getItems().addAll("LINEAR", "EASE_IN", "EASE_OUT", "EASE_BOTH"); interpolatorCombo.setValue("EASE_BOTH"); // Apply button Button applyBtn = new Button("Apply Changes"); applyBtn.setOnAction(e -> applyTimelineChanges()); // Layout grid.add(new Label("Duration (s):"), 0, 0); grid.add(durationSlider, 1, 0); grid.add(new Label("Cycle Count:"), 0, 1); grid.add(cycleCountSlider, 1, 1); grid.add(autoReverseCheck, 0, 2); grid.add(new Label("Interpolator:"), 0, 3); grid.add(interpolatorCombo, 1, 3); grid.add(applyBtn, 0, 4, 2, 1); return grid; } private void createTimeline() { timeline = new Timeline(); KeyFrame start = new KeyFrame(Duration.ZERO, new KeyValue(ball.centerXProperty(), 100), new KeyValue(ball.centerYProperty(), 200), new KeyValue(ball.fillProperty(), Color.BLUE) ); KeyFrame middle = new KeyFrame(Duration.seconds(1), new KeyValue(ball.centerXProperty(), 400), new KeyValue(ball.centerYProperty(), 100), new KeyValue(ball.fillProperty(), Color.RED) ); KeyFrame end = new KeyFrame(Duration.seconds(2), new KeyValue(ball.centerXProperty(), 700), new KeyValue(ball.centerYProperty(), 200), new KeyValue(ball.fillProperty(), Color.GREEN) ); timeline.getKeyFrames().addAll(start, middle, end); applyTimelineChanges(); timeline.play(); } private void applyTimelineChanges() { if (timeline != null) { timeline.stop(); // Update duration double duration = durationSlider.getValue(); updateKeyFrameDurations(duration); // Update cycle count int cycleCount = (int) cycleCountSlider.getValue(); timeline.setCycleCount(cycleCount); // Update auto reverse timeline.setAutoReverse(autoReverseCheck.isSelected()); // Update interpolator String interpolatorType = interpolatorCombo.getValue(); updateInterpolator(interpolatorType); timeline.play(); } } private void updateKeyFrameDurations(double totalDuration) { // Adjust keyframe times proportionally double middleTime = totalDuration / 2; double endTime = totalDuration; timeline.getKeyFrames().set(1, new KeyFrame(Duration.seconds(middleTime), new KeyValue(ball.centerXProperty(), 400), new KeyValue(ball.centerYProperty(), 100), new KeyValue(ball.fillProperty(), Color.RED) )); timeline.getKeyFrames().set(2, new KeyFrame(Duration.seconds(endTime), new KeyValue(ball.centerXProperty(), 700), new KeyValue(ball.centerYProperty(), 200), new KeyValue(ball.fillProperty(), Color.GREEN) )); } private void updateInterpolator(String interpolatorType) { Interpolator interpolator = switch (interpolatorType) { case "LINEAR" -> Interpolator.LINEAR; case "EASE_IN" -> Interpolator.EASE_IN; case "EASE_OUT" -> Interpolator.EASE_OUT; case "EASE_BOTH" -> Interpolator.EASE_BOTH; default -> Interpolator.LINEAR; }; // Update all key values with new interpolator for (KeyFrame frame : timeline.getKeyFrames()) { for (KeyValue keyValue : frame.getValues()) { // Note: KeyValue interpolator is final, so we'd need to recreate KeyFrames // This is a simplified demonstration } } } public static void main(String[] args) { launch(args); } }

Performance Optimization Tips

Example 8: Efficient Animation Techniques

import javafx.animation.*; 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.stage.Stage; import javafx.util.Duration; public class EfficientAnimationDemo extends Application { private static final int NUM_OBJECTS = 50; @Override public void start(Stage primaryStage) { Pane root = new Pane(); Scene scene = new Scene(root, 800, 600); // Create multiple animated objects for (int i = 0; i < NUM_OBJECTS; i++) { Rectangle rect = createAnimatedRectangle(i); root.getChildren().add(rect); } primaryStage.setTitle("Efficient Multiple Animations"); primaryStage.setScene(scene); primaryStage.show(); } private Rectangle createAnimatedRectangle(int index) { Rectangle rect = new Rectangle(30, 30); rect.setFill(Color.hsb((index * 360.0 / NUM_OBJECTS), 0.8, 0.8)); rect.setStroke(Color.BLACK); // Position based on index double startX = 50 + (index % 10) * 70; double startY = 50 + (index / 10) * 70; rect.setX(startX); rect.setY(startY); // Create efficient animation createEfficientAnimation(rect, startX, startY); return rect; } private void createEfficientAnimation(Rectangle rect, double startX, double startY) { Timeline timeline = new Timeline(); // Use fewer keyframes for better performance KeyFrame start = new KeyFrame(Duration.ZERO, new KeyValue(rect.xProperty(), startX), new KeyValue(rect.yProperty(), startY), new KeyValue(rect.rotateProperty(), 0) ); KeyFrame middle = new KeyFrame(Duration.seconds(1), new KeyValue(rect.xProperty(), startX + 200), new KeyValue(rect.yProperty(), startY + 100), new KeyValue(rect.rotateProperty(), 180) ); KeyFrame end = new KeyFrame(Duration.seconds(2), new KeyValue(rect.xProperty(), startX), new KeyValue(rect.yProperty(), startY), new KeyValue(rect.rotateProperty(), 360) ); timeline.getKeyFrames().addAll(start, middle, end); // Stagger start times for better performance timeline.setDelay(Duration.millis(Math.random() * 1000)); timeline.setCycleCount(Timeline.INDEFINITE); timeline.play(); } public static void main(String[] args) { launch(args); } }

Key Best Practices

  1. Use Appropriate Interpolators: Choose interpolators that match your animation needs
  2. Optimize KeyFrames: Use fewer keyframes when possible for better performance
  3. Manage Timeline Lifecycle: Always stop timelines when they're no longer needed
  4. Use Sequential/Parallel: Combine timelines for complex animations
  5. Consider Performance: Be mindful of animating many properties simultaneously
  6. Handle Exceptions: Implement proper error handling for animation failures

Conclusion

JavaFX Timeline provides a powerful and flexible framework for creating complex animations. Key takeaways:

  • KeyFrames and KeyValues define property changes over time
  • Interpolators control the pacing of animations
  • Timeline controls allow precise animation management
  • Sequential and parallel transitions enable complex animation sequences
  • Performance optimization is crucial for smooth animations

With these techniques, you can create everything from simple property animations to complex, interactive animation sequences in your JavaFX applications.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper