JavaFX Tutorial - JavaFX Transitions








Animation in JavaFX can be divided into timeline animation and transitions.

Timeline and Transition are subclasses of the javafx.animation.Animation class.

Transitions in JavaFX provide the means to incorporate animations in an internal timeline. JavaFX has built-in transition animations, which are convenience classes to perform common animated effects. Here are some of the most common animation transitions classes.

  • javafx.animation.FadeTransition
  • javafx.animation.PathTransition
  • javafx.animation.ScaleTransition
  • javafx.animation.TranslateTransition

The fade transition defined in FadeTransition targets the node's opacity property for a fading animation effect.

The path transition defined in PathTransition enables a node to follow a generated path.

Scale transition defined in ScaleTransition targets a node's scaleX, scaleY, and scaleZ properties to resize a node.

Translate transition defined in TranslateTransition targets a node's translateX, translateY, and translateZ properties to move a node across the screen.





Fade Transition

import javafx.animation.FadeTransition;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
//w w w .  j ava2s  .  c om
public class Main extends Application {

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

  @Override
  public void start(Stage primaryStage) {
    Group group = new Group();

    Rectangle rect = new Rectangle(20,20,200,200);
    
    FadeTransition ft = new FadeTransition(Duration.millis(5000), rect);
    ft.setFromValue(1.0);
    ft.setToValue(0.0);
    ft.play();
    
    group.getChildren().add(rect);
    
    Scene scene = new Scene(group, 300, 200);
    primaryStage.setScene(scene);
    primaryStage.show();
  }
}

The code above generates the following result.

null




FillTransition

import javafx.animation.FillTransition;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.effect.DropShadow;
import javafx.scene.paint.Color;
import javafx.scene.shape.Ellipse;
import javafx.stage.Stage;
import javafx.util.Duration;
/*  w w w . j  a  v a2  s  .  co m*/
public class Main extends Application {
  public static void main(String[] args) {
    Application.launch(args);
  }

  @Override
  public void start(Stage primaryStage) {
    primaryStage.setTitle("");
    Group root = new Group();
    Scene scene = new Scene(root, 300, 250, Color.WHITE);

    Group g = new Group();

    DropShadow ds = new DropShadow();
    ds.setOffsetY(3.0);
    ds.setColor(Color.color(0.4, 0.4, 0.4));

    Ellipse ellipse = new Ellipse();
    ellipse.setCenterX(50.0f);
    ellipse.setCenterY(50.0f);
    ellipse.setRadiusX(50.0f);
    ellipse.setRadiusY(25.0f);
    ellipse.setEffect(ds);

    FillTransition ft = new FillTransition(Duration.millis(3000), ellipse, Color.RED, Color.BLUE);
    ft.setAutoReverse(true);
    ft.play();
    
    g.getChildren().add(ellipse);

    root.getChildren().add(g);
    primaryStage.setScene(scene);
    primaryStage.show();
  }
}

The code above generates the following result.

null

Path Transition

A path transition moves a node along a path from one end to the other over a given time.

/*//from   w w  w .j av  a  2s .c  om
 * Copyright (c) 2011, Pro JavaFX Authors
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of JFXtras nor the names of its contributors may be used
 *    to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * Metronome1Main.fx - A simple example of animation using a Timeline
 *
 *  Developed 2011 by James L. Weaver jim.weaver [at] javafxpert.com
 *  as a JavaFX SDK 2.0 example for the Pro JavaFX book.
 */
//package projavafx.metronomepathtransition.ui;

import javafx.animation.Animation;
import javafx.animation.Interpolator;
import javafx.animation.PathTransition;
import javafx.animation.PathTransition.OrientationType;
import javafx.animation.PathTransitionBuilder;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.GroupBuilder;
import javafx.scene.Scene;
import javafx.scene.SceneBuilder;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBuilder;
import javafx.scene.layout.HBoxBuilder;
import javafx.scene.paint.Color;
import javafx.scene.shape.ArcToBuilder;
import javafx.scene.shape.Ellipse;
import javafx.scene.shape.EllipseBuilder;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.PathBuilder;
import javafx.stage.Stage;
import javafx.util.Duration;

public class Main extends Application {
  Button startButton;
  Button pauseButton;
  Button resumeButton;
  Button stopButton;
  Ellipse ellipse = EllipseBuilder.create()
    .centerX(100)
    .centerY(50)
    .radiusX(4)
    .radiusY(8)
    .fill(Color.BLUE)
    .build();
  
  Path path = PathBuilder.create()
    .elements(
      new MoveTo(100, 50),
      ArcToBuilder.create()
        .x(300)
        .y(50)
        .radiusX(350)
        .radiusY(350)
        .sweepFlag(true)
        .build()
    )
    .build();
  
  PathTransition anim = PathTransitionBuilder.create()
    .duration(new Duration(1000.0))
    .node(ellipse)
    .path(path)
    .orientation(OrientationType.ORTHOGONAL_TO_TANGENT)
    .interpolator(Interpolator.LINEAR)
    .autoReverse(true)
    .cycleCount(Timeline.INDEFINITE)
    .build();
  
  public static void main(String[] args) {
    Application.launch(args);
  }
  
  @Override
  public void start(Stage stage) {
    Scene scene  = SceneBuilder.create()
      .width(400)
      .height(500)
      .root(
        GroupBuilder.create()
          .children(
            ellipse,
            HBoxBuilder.create()
              .layoutX(60)
              .layoutY(420)
              .spacing(10)
              .children(
                startButton = ButtonBuilder.create()
                  .text("Start")
                  .onAction(new EventHandler<ActionEvent>() {
                    @Override public void handle(ActionEvent e) {
                      anim.playFromStart();
                    }
                  })
                  .build(),
                pauseButton = ButtonBuilder.create()
                  .text("Pause")
                  .onAction(new EventHandler<ActionEvent>() {
                    @Override public void handle(ActionEvent e) {
                      anim.pause();
                    }
                  })
                  .build(),
                resumeButton = ButtonBuilder.create()
                  .text("Resume")
                  .onAction(new EventHandler<ActionEvent>() {
                    @Override public void handle(ActionEvent e) {
                      anim.play();
                    }
                  })
                  .build(),
                stopButton = ButtonBuilder.create()
                  .text("Stop")
                  .onAction(new EventHandler<ActionEvent>() {
                    @Override public void handle(ActionEvent e) {
                      anim.stop();
                    }
                  })
                  .build()
              )
              .build()
          )
          .build()
      )
      .build();

    startButton.disableProperty().bind(anim.statusProperty()
            .isNotEqualTo(Animation.Status.STOPPED));
    pauseButton.disableProperty().bind(anim.statusProperty()
            .isNotEqualTo(Animation.Status.RUNNING));
    resumeButton.disableProperty().bind(anim.statusProperty()
            .isNotEqualTo(Animation.Status.PAUSED));
    stopButton.disableProperty().bind(anim.statusProperty()
            .isEqualTo(Animation.Status.STOPPED));
    
    stage.setScene(scene);
    stage.setTitle("Metronome using PathTransition");
    stage.show();
  }
}

The code above generates the following result.

null

Animation along a path

import javafx.animation.PathTransition;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.CubicCurveTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.stage.Stage;
import javafx.util.Duration;
//from www  . j a va 2 s  .  c  o  m
public class Main extends Application {

  @Override
  public void start(final Stage stage) throws Exception {
    final Group group = new Group();
    final Scene scene = new Scene(group, 600, 400, Color.GHOSTWHITE);
    stage.setScene(scene);
    stage.setTitle("JavaFX 2 Animations");
    stage.show();
    final Circle circle = new Circle(20, 20, 15);
    circle.setFill(Color.DARKRED);

    final Path path = new Path();
    path.getElements().add(new MoveTo(20, 20));
    path.getElements().add(new CubicCurveTo(30, 10, 380, 120, 200, 120));
    path.getElements().add(new CubicCurveTo(200, 1120, 110, 240, 380, 240));
    path.setOpacity(0.5);

    group.getChildren().add(path);
    group.getChildren().add(circle);
    final PathTransition pathTransition = new PathTransition();

    pathTransition.setDuration(Duration.seconds(8.0));
    pathTransition.setDelay(Duration.seconds(.5));
    pathTransition.setPath(path);
    pathTransition.setNode(circle);
    pathTransition
        .setOrientation(PathTransition.OrientationType.ORTHOGONAL_TO_TANGENT);
    pathTransition.setCycleCount(Timeline.INDEFINITE);
    pathTransition.setAutoReverse(true);
    pathTransition.play();
  }

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

The code above generates the following result.

null

Parallel Transition

A parallel transition executes several transitions simultaneously.

/*//from   ww w . j  ava2 s . co  m
 * Copyright (c) 2011, Pro JavaFX Authors
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of JFXtras nor the names of its contributors may be used
 *    to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * Metronome1Main.fx - A simple example of animation using a Timeline
 *
 *  Developed 2011 by James L. Weaver jim.weaver [at] javafxpert.com
 *  as a JavaFX SDK 2.0 example for the Pro JavaFX book.
 */
import javafx.animation.FadeTransition;
import javafx.animation.ParallelTransition;
import javafx.animation.RotateTransition;
import javafx.animation.ScaleTransition;
import javafx.animation.Timeline;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;

public class Main extends Application {

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

  @Override
  public void start(Stage primaryStage) {
    Group group = new Group();

    Rectangle rectParallel = new Rectangle(20, 20, 200, 200);

    FadeTransition fadeTransition = new FadeTransition(Duration.millis(3000),
        rectParallel);
    fadeTransition.setFromValue(1.0f);
    fadeTransition.setToValue(0.3f);
    fadeTransition.setCycleCount(2);
    fadeTransition.setAutoReverse(true);
    TranslateTransition translateTransition = new TranslateTransition(
        Duration.millis(2000), rectParallel);
    translateTransition.setFromX(50);
    translateTransition.setToX(350);
    translateTransition.setCycleCount(2);
    translateTransition.setAutoReverse(true);
    RotateTransition rotateTransition = new RotateTransition(
        Duration.millis(3000), rectParallel);
    rotateTransition.setByAngle(180f);
    rotateTransition.setCycleCount(4);
    rotateTransition.setAutoReverse(true);
    ScaleTransition scaleTransition = new ScaleTransition(
        Duration.millis(2000), rectParallel);
    scaleTransition.setToX(2f);
    scaleTransition.setToY(2f);
    scaleTransition.setCycleCount(2);
    scaleTransition.setAutoReverse(true);

    ParallelTransition parallelTransition = new ParallelTransition();
    parallelTransition.getChildren().addAll(fadeTransition,
        translateTransition, rotateTransition, scaleTransition);
    parallelTransition.setCycleCount(Timeline.INDEFINITE);
    parallelTransition.play();

    group.getChildren().add(rectParallel);

    Scene scene = new Scene(group, 300, 200);
    primaryStage.setScene(scene);
    primaryStage.show();
  }
}

The code above generates the following result.

null

Sequential Transition

A sequential transition executes several transitions one after another.

/*//from  w ww .  jav a2  s.  c o m
 * Copyright (c) 2011, Pro JavaFX Authors
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of JFXtras nor the names of its contributors may be used
 *    to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * Metronome1Main.fx - A simple example of animation using a Timeline
 *
 *  Developed 2011 by James L. Weaver jim.weaver [at] javafxpert.com
 *  as a JavaFX SDK 2.0 example for the Pro JavaFX book.
 */
import javafx.animation.FadeTransition;
import javafx.animation.RotateTransition;
import javafx.animation.ScaleTransition;
import javafx.animation.SequentialTransition;
import javafx.animation.Timeline;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;

public class Main extends Application {

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

  @Override
  public void start(Stage primaryStage) {
    Group group = new Group();

    Rectangle rectSeq = new Rectangle(20, 20, 200, 200);

    FadeTransition fadeTransition = new FadeTransition(Duration.millis(1000),
        rectSeq);
    fadeTransition.setFromValue(1.0f);
    fadeTransition.setToValue(0.3f);
    fadeTransition.setCycleCount(1);
    fadeTransition.setAutoReverse(true);

    TranslateTransition translateTransition = new TranslateTransition(
        Duration.millis(2000), rectSeq);
    translateTransition.setFromX(50);
    translateTransition.setToX(375);
    translateTransition.setCycleCount(1);
    translateTransition.setAutoReverse(true);

    RotateTransition rotateTransition = new RotateTransition(
        Duration.millis(2000), rectSeq);
    rotateTransition.setByAngle(180f);
    rotateTransition.setCycleCount(4);
    rotateTransition.setAutoReverse(true);

    ScaleTransition scaleTransition = new ScaleTransition(
        Duration.millis(2000), rectSeq);
    scaleTransition.setFromX(1);
    scaleTransition.setFromY(1);
    scaleTransition.setToX(2);
    scaleTransition.setToY(2);
    scaleTransition.setCycleCount(1);
    scaleTransition.setAutoReverse(true);

    SequentialTransition sequentialTransition = new SequentialTransition();
    sequentialTransition.getChildren().addAll(fadeTransition,
        translateTransition, rotateTransition, scaleTransition);
    sequentialTransition.setCycleCount(Timeline.INDEFINITE);
    sequentialTransition.setAutoReverse(true);

    sequentialTransition.play();

    group.getChildren().add(rectSeq);

    Scene scene = new Scene(group, 300, 200);
    primaryStage.setScene(scene);
    primaryStage.show();
  }
}

The code above generates the following result.

null

PauseTransition

import javafx.animation.FadeTransition;
import javafx.animation.PauseTransition;
import javafx.animation.RotateTransition;
import javafx.animation.ScaleTransition;
import javafx.animation.SequentialTransition;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
/*  w w  w. j a  v a  2  s .com*/
public class Main extends Application {

  @Override
  public void start(Stage stage) {
    Group root = new Group();
    Scene scene = new Scene(root, 600, 400);
    stage.setScene(scene);
    stage.setTitle("");

    Rectangle rect = new Rectangle(100, 40, 100, 100);
    rect.setArcHeight(50);
    rect.setArcWidth(50);
    rect.setFill(Color.VIOLET);


    PauseTransition pt = new PauseTransition(Duration.millis(1000));
    FadeTransition ft = new FadeTransition(Duration.millis(2000));
    ft.setFromValue(1.0f);
    ft.setToValue(0.3f);
    ft.setAutoReverse(true);
    TranslateTransition tt = new TranslateTransition(Duration.millis(2000));
    tt.setFromX(-100f);
    tt.setToX(100f);
    tt.setAutoReverse(true);
    RotateTransition rt = new RotateTransition(Duration.millis(2000));
    rt.setByAngle(180f);
    rt.setAutoReverse(true);
    ScaleTransition st = new ScaleTransition(Duration.millis(2000));
    st.setByX(1.5f);
    st.setByY(1.5f);
    st.setAutoReverse(true);

    SequentialTransition seqT = new SequentialTransition(rect, pt, ft, tt, rt,
        st);
    seqT.play();

    root.getChildren().add(rect);

    stage.show();
  }

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

The code above generates the following result.

null

Text typing effect

import javafx.animation.Animation;
import javafx.animation.Transition;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.util.Duration;
// w ww.  j  a  v  a  2s.c  o  m
public class Main extends Application {
  public static void main(String[] args) {
    launch(args);
  }

  @Override
  public void start(Stage stage) {
    Scene scene = new Scene(new Group());
    stage.setTitle("Sample");
    stage.setWidth(300);
    stage.setHeight(190);

    VBox vbox = new VBox();
    vbox.setLayoutX(20);
    vbox.setLayoutY(20);
    

    final String content = "Lorem ipsum";
    final Text text = new Text(10, 20, "");
    
    final Animation animation = new Transition() {
        {
            setCycleDuration(Duration.millis(2000));
        }
    
        protected void interpolate(double frac) {
            final int length = content.length();
            final int n = Math.round(length * (float) frac);
            text.setText(content.substring(0, n));
        }
    
    };
    
    animation.play();

    

    vbox.getChildren().add(text);
    vbox.setSpacing(10);
    ((Group) scene.getRoot()).getChildren().add(vbox);

    stage.setScene(scene);
    stage.show();
  }
}

The code above generates the following result.

null