© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2022
K. Sharan, P. SpäthLearn JavaFX 17https://doi.org/10.1007/978-1-4842-7848-2_19

19. Understanding Animation

Kishori Sharan1   and Peter Späth2
(1)
Montgomery, AL, USA
(2)
Leipzig, Sachsen, Germany
 
In this chapter, you will learn:
  • What animation is in JavaFX

  • About classes in JavaFX that are used in performing animation in JavaFX

  • How to perform a timeline animation and how to set up cue points on a timeline animation

  • How to control animation such as playing, reversing, pausing, and stopping

  • How to perform animation using transitions

  • About different types of interpolators and their roles in animation

The examples of this chapter lie in the com.jdojo.animation package. In order for them to work, you must add a corresponding line to the module-info.java file:
...
opens com.jdojo.animation to javafx.graphics, javafx.base;
...

What Is Animation?

In the real world, animation implies some kind of motion, which is generated by displaying images in quick succession. For example, when you watch a movie, you are watching images, which change so quickly that you get an illusion of motion.

In JavaFX, animation is defined as changing the property of a node over time. If the property that changes determines the location of the node, the animation in JavaFX will produce an illusion of motion as found in movies. Not all animations have to involve motion; for example, changing the fill property of a shape over time is an animation in JavaFX that does not involve motion.

To understand how animation is performed, it is important to understand some key concepts:
  • Timeline

  • Key frame

  • Key value

  • Interpolator

Animation is performed over a period of time. A timeline denotes the progression of time during animation with an associated key frame at a given instant. A key frame represents the state of the node being animated at a specific instant on the timeline. A key frame has associated key values. A key value represents the value of a property of the node along with an interpolator to be used.

Suppose you want to move a circle in a scene from left to right horizontally in ten seconds. Figure 19-1 shows the circle at some positions. The thick horizontal line represents a timeline. Circles with a solid outline represent the key frames at specific instants on the timeline. The key values associated with key frames are shown at the top line. For example, the value for the translateX property of the circle for the key frame at the fifth second is 500, which is shown as tx=500 in the figure.
Figure 19-1

Animating a circle along a horizontal line using a timeline

The developer provides timelines, key frames, and key values. In this example, there are five key frames. If JavaFX shows only five key frames at the five respective instants, the animation will look jerky. To provide a smooth animation, JavaFX needs to interpolate the position of the circle at any instant on the timeline. That is, JavaFX needs to create intermediate key frames between two consecutive provided key frames. JavaFX does this with the help of an interpolator. By default, it uses a linear interpolator , which changes the property being animated linearly with time. That is, if the time on the timeline passes x%, the value of the property will be x% between the initial and final target values. Circles with the dashed outline are created by JavaFX using an interpolator.

Understanding Animation Classes

Classes providing animation in JavaFX are in the javafx.animation package, except the Duration class, which is in the javafx.util package. Figure 19-2 shows a class diagram for most of the animation-related classes.
Figure 19-2

A class diagram for core classes used in animation

The abstract Animation class represents an Animation. It contains common properties and methods used by all types of animation.

JavaFX supports two types of animations:
  • Timeline animations

  • Transitions

In a timeline animation, you create a timeline and add key frames to it. JavaFX creates the intermediate key frames using an interpolator. An instance of the Timeline class represents a timeline animation. This type of animation requires a little more code, but it gives you more control.

Several types of animations are commonly performed (moving a node along a path, changing the opacity of a node over time, etc.). These types of animations are known as transitions. They are performed using an internal timeline. An instance of the Transition class represents a transition animation. Several subclasses of the Transition class exist to support specific types of transitions. For example, the FadeTransition class implements a fading effect animation by changing the opacity of a node over time. You create an instance of the Transition class (typically, an instance of one of its subclasses) and specify the initial and final values for the property to be animated and the duration for the animation. JavaFX takes care of creating the timeline and performing the animation. This type of animation is easier to use.

Sometimes, you may want to perform multiple transitions sequentially or simultaneously. The SequentialTransition and ParallelTransition classes let you perform a set of transitions sequentially and simultaneously, respectively.

Understanding Utility Classes

Before discussing the details of JavaFX animation, I will discuss a few utility classes that are used in implementing animations. The following sections will discuss those classes.

Understanding the Duration Class

The Duration class is in the javafx.util package. It represents a duration of time in milliseconds, seconds, minutes, and hours. It is an immutable class. A Duration represents the amount of time for each cycle of an animation. A Duration can represent a positive or negative duration.

You can create a Duration object in three ways:
  • Using the constructor

  • Using factory methods

  • Using the valueOf() method from a duration in String format

The constructor takes the amount of time in milliseconds:
Duration tenMillis = new Duration(10);
Factory methods create Duration objects for different units of time. They are millis(), seconds(), minutes(), and hours():
Duration tenMillis = Duration.millis(10);
Duration tenSeconds = Duration.seconds(10);
Duration tenMinutes = Duration.minutes(10);
Duration tenHours = Duration.hours(10);
The valueOf() static method takes a String argument containing the duration of time and returns a Duration object. The format of the argument is “number[ms|s|m|h]”, where number is the amount of time, and ms, s, m, and h denote milliseconds, seconds, minutes, and hours, respectively.
Duration tenMillis = Duration.valueOf("10.0ms");
Duration tenMillisNeg = Duration.valueOf("-10.0ms");

You can also represent a duration of an unknown amount of time and an indefinite time using the UNKNOWN and INDEFINITE constants of the Duration class, respectively. You can use the isIndefinite() and isUnknown() methods to check if a duration represents an indefinite or unknown amount of time. The class declares two more constants, ONE and ZERO, that represent durations of one millisecond and zero (no time), respectively.

The Duration class provides several methods to manipulate durations (adding a duration to another duration, dividing and multiplying a duration by a number, comparing two durations, etc.). Listing 19-1 shows how to use the Duration class .
// DurationTest.java
package com.jdojo.animation;
import javafx.util.Duration;
public class DurationTest {
        public static void main(String[] args) {
                Duration d1 = Duration.seconds(30.0);
                Duration d2 = Duration.minutes(1.5);
                Duration d3 = Duration.valueOf("35.25ms");
                System.out.println("d1  = " + d1);
                System.out.println("d2  = " + d2);
                System.out.println("d3  = " + d3);
                System.out.println("d1.toMillis() = " + d1.toMillis());
                System.out.println("d1.toSeconds() = " + d1.toSeconds());
                System.out.println("d1.toMinutes() = " + d1.toMinutes());
                System.out.println("d1.toHours() = " + d1.toHours());
                System.out.println("Negation of d1  = " + d1.negate());
                System.out.println("d1 + d2 = " + d1.add(d2));
                System.out.println("d1 / 2.0 = " + d1.divide(2.0));
                Duration inf = Duration.millis(1.0/0.0);
                Duration unknown = Duration.millis(0.0/0.0);
                System.out.println("inf.isIndefinite() = " +
                         inf.isIndefinite());
                System.out.println("unknown.isUnknown() = " +
                         unknown.isUnknown());
        }
}
d1  = 30000.0 ms
d2  = 90000.0 ms
d3  = 35.25 ms
d1.toMillis() = 30000.0
d1.toSeconds() = 30.0
d1.toMinutes() = 0.5
d1.toHours() = 0.008333333333333333
Negation of d1  = -30000.0 ms
d1 + d2 = 120000.0 ms
d1 / 2.0 = 15000.0 ms
inf.isIndefinite() = true
unknown.isUnknown() = true
Listing 19-1

Using the Duration Class

Understanding the KeyValue Class

An instance of the KeyValue class represents a key value that is interpolated for a particular interval during animation. It encapsulates three things:
  • A target

  • An end value for the target

  • An interpolator

The target is a WritableValue, which qualifies all JavaFX properties to be a target. The end value is the value for the target at the end of the interval. The interpolator is used to compute the intermediate key frames.

A key frame contains one or more key values, and it defines a specific point on a timeline. Figure 19-3 shows an interval on a timeline. The interval is defined by two instants: instant1 and instant2. Both instants have an associated key frame; each key frame contains a key value. An animation may progress forward or backward on the timeline. When an interval starts, the end value of the target is taken from the key value of the end key frame of the interval, and its interpolator is used to compute the intermediate key frames. Suppose, in the figure, the animation is progressing in the forward direction and instant1 occurs before instant2. From instant1 to instant2, the interpolator of the key-value2 will be used to compute the key frames for the interval. If the animation is progressing in the backward direction, the interpolator of the key-value1 will be used to compute the intermediate key frames from instant2 to instant1.
Figure 19-3

Key frames at two instants on a timeline

The KeyValue class is immutable. It provides two constructors:
  • KeyValue(WritableValue<T> target, T endValue)

  • KeyValue(WritableValue<T> target, T endValue, Interpolator interpolator)

The Interpolator.LINEAR is used as the default interpolator that interpolates the animated property linearly with time. I will discuss different types of interpolators later.

The following snippet of code creates a Text object and two KeyValue objects. The translateX property is the target. 0 and 100 are the end values for the target. The default interpolator is used:
Text msg = new Text("JavaFX animation is cool!");
KeyValue initKeyValue = new KeyValue(msg.translateXProperty(), 0.0);
KeyValue endKeyValue = new KeyValue(msg.translateXProperty(), 100.0);
The following snippet of code is similar to the one shown earlier. It uses the Interpolator.EASE_BOTH interpolator, which slows down the animation in the start and toward the end:
Text msg = new Text("JavaFX animation is cool!");
KeyValue initKeyValue = new KeyValue(msg.translateXProperty(), 0.0,
    Interpolator.EASE_BOTH);
KeyValue endKeyValue = new KeyValue(msg.translateXProperty(), 100.0,
    Interpolator.EASE_BOTH);

Understanding the KeyFrame Class

A key frame defines the target state of a node at a specified point on the timeline. The target state is defined by the key values associated with the key frame.

A key frame encapsulates four things:
  • An instant on the timeline

  • A set of KeyValues

  • A name

  • An ActionEvent handler

The instant on the timeline with which the key frame is associated is defined by a Duration, which is an offset of the key frame on the timeline.

The set of KeyValues defines the end value of the target for the key frame.

A key frame may optionally have a name that can be used as a cue point to jump to the instant defined by it during an animation. The getCuePoints() method of the Animation class returns a Map of cue points on the Timeline.

Optionally, you can attach an ActionEvent handler to a KeyFrame. The ActionEvent handler is called when the time for the key frame arrives during animation.

An instance of the KeyFrame class represents a key frame. The class provides several constructors:
  • KeyFrame(Duration time, EventHandler<ActionEvent> onFinished, KeyValue... values)

  • KeyFrame(Duration time, KeyValue... values)

  • KeyFrame(Duration time, String name, EventHandler<ActionEvent> onFinished, Collection<KeyValue> values)

  • KeyFrame(Duration time, String name, EventHandler<ActionEvent> onFinished, KeyValue... values)

  • KeyFrame(Duration time, String name, KeyValue... values)

The following snippet of code creates two instances of KeyFrame that specify the translateX property of a Text node at zero seconds and three seconds on a timeline:
Text msg = new Text("JavaFX animation is cool!");
KeyValue initKeyValue = new KeyValue(msg.translateXProperty(), 0.0);
KeyValue endKeyValue = new KeyValue(msg.translateXProperty(), 100.0);
KeyFrame initFrame = new KeyFrame(Duration.ZERO, initKeyValue);
KeyFrame endFrame = new KeyFrame(Duration.seconds(3), endKeyValue);

Understanding the Timeline Animation

A timeline animation is used for animating any properties of a node. An instance of the Timeline class represents a timeline animation. Using a timeline animation involves the following steps:
  • Construct key frames.

  • Create a Timeline object with key frames.

  • Set the animation properties.

  • Use the play() method to run the animation.

You can add key frames to a Timeline at the time of creating it or after. The Timeline instance keeps all key frames in an ObservableList<KeyFrame> object. The getKeyFrames() method returns the list. You can modify the list of key frames at any time. If the timeline animation is already running, you need to stop and restart it to pick up the modified list of key frames.

The Timeline class contains several constructors:
  • Timeline()

  • Timeline(double targetFramerate)

  • Timeline(double targetFramerate, KeyFrame... keyFrames)

  • Timeline(KeyFrame... keyFrames)

The no-args constructor creates a Timeline with no key frames with animation running at the optimum rate. Other constructors let you specify the target frame rate for the animation, which is the number of frames per second, and the key frames.

Note that the order in which the key frames are added to a Timeline is not important. Timeline will order them based on their time offset.

The program in Listing 19-2 starts a timeline animation that scrolls a text horizontally from right to left across the scene forever. Figure 19-4 shows a screenshot of the animation.
// ScrollingText.java
// ...find in the book's download area.
Listing 19-2

Scrolling Text Using a Timeline Animation

Figure 19-4

Scrolling text using a timeline animation

The logic to perform the animation is in the start() method. The method starts with creating a Text object, a Pane with the Text object, and setting up a scene for the stage. After showing the stage, it sets up an animation.

It gets the width of the scene and the Text object:
double sceneWidth = scene.getWidth();
double msgWidth = msg.getLayoutBounds().getWidth();
Two key frames are created: one for time = 0 seconds and one for time = 3 seconds. The animation uses the translateX property of the Text object to change its horizontal position to make it scroll. At zero seconds, the Text is positioned at the scene width, so it is invisible. At three seconds, it is placed to the left of the scene at a distance equal to its length, so again it is invisible:
KeyValue initKeyValue = new KeyValue(msg.translateXProperty(), sceneWidth);
KeyFrame initFrame = new KeyFrame(Duration.ZERO, initKeyValue);
KeyValue endKeyValue = new KeyValue(msg.translateXProperty(), -1.0 * msgWidth);
KeyFrame endFrame = new KeyFrame(Duration.seconds(3), endKeyValue);
A Timeline object is created with two key frames:
Timeline timeline = new Timeline(initFrame, endFrame);
By default, the animation will run only one time. That is, the Text will scroll from right to left once, and the animation will stop. You can set the cycle count for an animation, which is the number of times the animation needs to run. You run the animation forever by setting the cycle count to Timeline.INDEFINITE:
timeline.setCycleCount(Timeline.INDEFINITE);
Finally, the animation is started by calling the play() method:
timeline.play();
Our example has a flaw. The scrolling of the text does not update its initial horizontal position when the width of the scene changes. You can rectify this problem by updating the initial key frame whenever the scene width changes. Append the following statement to the start() method of Listing 19-2. It adds a ChangeListener for the scene width that updates key frames and restarts the animation:
scene.widthProperty().addListener( (prop, oldValue , newValue) -> {
        KeyValue kv = new KeyValue(msg.translateXProperty(),
              scene.getWidth());
        KeyFrame kf = new KeyFrame(Duration.ZERO, kv);
        timeline.stop();
        timeline.getKeyFrames().clear();
        timeline.getKeyFrames().addAll(kf, endFrame);
        timeline.play();
});
It is possible to create a Timeline animation with only one key frame. The key frame is treated as the last key frame. The Timeline synthesizes an initial key frame (for time = 0 seconds) using the current values for the WritableValue being animated. To see the effect, let us replace the statement
Timeline timeline = new Timeline(initFrame, endFrame);
in Listing 19-2 with the following:
Timeline timeline = new Timeline(endFrame);

The Timeline will create an initial key frame with the current value of the translateX property of the Text object, which is 0.0. This time, the Text scrolls differently. The scrolling starts by placing the Text at 0.0 and scrolling it to the left, so it goes beyond the scene.

Controlling an Animation

The Animation class contains properties and methods that can be used to control animation in various ways. The following sections will explain those properties and methods and how to use them to control animation.

Playing an Animation

The Animation class contains four methods to play an animation:
  • play()

  • playFrom(Duration time)

  • playFrom(String cuePoint)

  • playFromStart()

The play() method plays an animation from its current position. If the animation was never started or stopped, it will play from the beginning. If the animation was paused, it will play from the position where it was paused. You can use the jumpTo(Duration time) and jumpTo(String cuePoint) methods to set the current position of the animation to a specific duration or a cue point, before calling the play() method. Calling the play() method is asynchronous. The animation may not start immediately. Calling the play() method while animation is running has no effect.

The playFrom() method plays an animation from the specified duration or the specified cue point. Calling this method is equivalent to setting the current position using the jumpTo() method and then calling the play() method.

The playFromStart() method plays the animation from the beginning (duration = 0).

Delaying the Start of an Animation

You can specify a delay in starting the animation using the delay property. The value is specified in Duration. By default, it is zero milliseconds:
Timeline timeline = ...
// Delay the start of the animation by 2 seconds
timeline.setDelay(Duration.seconds(2));
// Play the animation
timeline.play();

Stopping an Animation

Use the stop() method to stop a running animation. The method has no effect if the animation is not running. The animation may not stop immediately when the method is called as the method executes asynchronously. The method resets the current position to the beginning. That is, calling play() after stop() will play the animation from the beginning:
Timeline timeline = ...
...
timeline.play();
...
timeline.stop();

Pausing an Animation

Use the pause() method to pause an animation. Calling this method when animation is not running has no effect. This method executes asynchronously. Calling the play() method when the animation is paused plays it from the current position. If you want to play the animation from the start, call the playFromStart() method .

Knowing the State of an Animation

An animation can be one of the following three states:
  • Running

  • Paused

  • Stopped

The three states are represented by RUNNING, STOPPED, and PAUSED constants of the Animation.Status enum. You do not change the state of an animation directly. It is changed by calling one of the methods of the Animation class. The class contains a read-only status property that can be used to know the state of the animation at any time:
Timeline timeline = ...
...
Animation.Status status = timeline.getStatus();
switch(status) {
        case RUNNING:
                System.out.println("Running");
                break;
        case STOPPED:
                System.out.println("Stopped");
                break;
        case PAUSED:
                System.out.println("Paused");
                break;
}

Looping an Animation

An animation can cycle multiple times, even indefinitely. The cycleCount property specifies the number of cycles in an animation, which defaults to one. If you want to run the animation in an infinite loop, specify Animation.INDEFINITE as the cycleCount. The cycleCount must be set to a value greater than zero. If the cycleCount is changed while the animation is running, the animation must be stopped and restarted to pick up the new value:
Timeline timeline1 = ...
Timeline1.setCycleCount(Timeline.INDEFINITE); // Run the animation forever
Timeline timeline2 = ...
Timeline2.setCycleCount(2); // Run the animation for two cycles

Auto Reversing an Animation

By default, an animation runs only in the forward direction. For example, our scrolling text animation scrolled the text from right to left in one cycle. In the next cycle, the scrolling occurs again from right to left.

Using the autoReverse property, you can define whether the animation is performed in the reverse direction for alternating cycles. By default, it is set to false. Set it to true to reverse the direction of the animation:
Timeline timeline = ...
timeline.setAutoReverse(true); // Reverse direction on alternating cycles

If you change the autoReverse, you need to stop and restart the animation for the new value to take effect.

Attaching an onFinished Action

You can execute an ActionEvent handler when an animation finishes. Stopping the animation or terminating the application while the animation is running will not execute the handler. You can specify the handler in the onFinished property of the Animation class. The following snippet of code sets the onFinished property to an ActionEvent handler that prints a message on the standard output:
Timeline timeline = ...
timeline.setOnFinished(e -> System.out.print("Animation finished."));

Note that an animation with an Animation.INDEFINITE cycle count will not finish, and attaching such an action to the animation will never execute.

Knowing the Duration of an Animation

An animation involves two types of durations:
  • Duration to play one cycle of the animation

  • Duration to play all cycles of the animation

These durations are not set directly. They are set using other properties of the animation (cycle count, key frames, etc.).

The duration for one cycle is set using key frames. The key frame with the maximum duration determines the duration for one cycle when the animation is played at the rate 1.0. The read-only cycleDuration property of the Animation class reports the duration for one cycle.

The total duration for an animation is reported by the read-only totalDuration property. It is equal to cycleCount * cycleDuration. If the cycleCount is set to Animation.INDEFINITE, the totalDuration is reported as Duration.INDEFINITE.

Note that the actual duration for an animation depends on its play rate represented by the rate property. Because the play rate can be changed while animation is running, there is no easy way to compute the actual duration of an animation.

Adjusting the Speed of an Animation

The rate property of the Animation class specifies the direction and the speed for the animation. The sign of its value indicates the direction. The magnitude of the value indicates the speed. A positive value indicates the play in the forward direction. A negative value indicates the play in the backward direction. A value of 1.0 is considered the normal rate of play, a value of 2.0 double the normal rate, 0.50 half the normal rate, and so on. A rate of 0.0 stops the play.

It is possible to invert the rate of a running animation. In that case, the animation is played in the reverse direction from the current position for the duration that has already elapsed. Note that you cannot start an animation using a negative rate. An animation with a negative rate will not start. You can change the rate to be negative only when the animation has played for a while.
Timeline timeline = ...
// Play the animation at double the normal rate
Timeline.setRate(2.0);
...
timeline.play();
...
// Invert the rate of the play
timeline.setRate(-1.0 * timeline.getRate());

The read-only currentRate property indicates the current rate (the direction and speed) at which the animation is playing. The values for the rate and currentRate properties may not be equal. The rate property indicates the rate at which the animation is expected to play when it runs, whereas the currentRate indicates the rate at which the animation is being played. When the animation is stopped or paused, the currentRate value is 0.0. If the animation reverses its direction automatically, the currentRate will report a different direction during reversal; for example, if the rate is 1.0, the currentRate reports 1.0 for the forward play cycle and –1.0 for the reverse play cycle.

Understanding Cue Points

You can set up cue points on a timeline. Cue points are named instants on the timeline. An animation can jump to a cue point using the jumpTo(String cuePoint) method. An animation maintains an ObservableMap<String,Duration> of cue points. The key in the map is the name of the cue points, and the values are the corresponding duration on the timeline. Use the getCuePoints() method to get the reference of the cue points map.

There are two ways to add cue points to a timeline:
  • Giving a name to the KeyFrame you add to a timeline that adds a cue point in the cue point map

  • Adding name-duration pairs to the map returned by the getCuePoints() method of the Animation class

    Tip Every animation has two predefined cue points: “start” and “end.” They are set at the start and end of the animation. The two cue points do not appear in the map returned by the getCuePoints() method.

The following snippet of code creates a KeyFrame with a name “midway.” When it is added to a timeline, a cue point named “midway” will be added to the timeline automatically. You can jump to this KeyFrame using jumpTo("midway").
// Create a KeyFrame with name “midway”
KeyValue midKeyValue = ...
KeyFrame midFrame = new KeyFrame(Duration.seconds(5), "midway", midKeyValue);
The following snippet of code adds two cue points directly to the cue point map of a timeline:
Timeline timeline = ...
timeline.getCuePoints().put("3 seconds", Duration.seconds(3));
timeline.getCuePoints().put("7 seconds", Duration.seconds(7));
The program in Listing 19-3 shows how to add and use cue points on a timeline. It adds a KeyFrame with a “midway” name, which automatically becomes a cue point. It adds two cue points, “3 seconds” and “7 seconds,” directly to the cue point map. The list of available cue points is shown in a ListView on the left side of the screen. A Text object scrolls with a cycle duration of ten seconds. The program displays a window as shown in Figure 19-5. Select a cue point from the list, and the animation will start playing from that point.
// CuePointTest.java
// ...find in the book's download area.
Listing 19-3

Using Cue Points in Animation

Figure 19-5

Scrolling text with the list of cue points

Understanding Transitions

In the previous sections, you saw animations using a timeline that involved setting up key frames on the timeline. Using timeline animation is not easy in all cases. Consider moving a node in a circular path. Creating key frames and setting up a timeline to move the node on the circular path are not easy. JavaFX contains a number of classes (known as transitions) that let you animate nodes using predefined properties.

All transition classes inherit from the Transition class, which, in turn, inherits from the Animation class. All methods and properties in the Animation class are also available for use in creating transitions. The transition classes take care of creating the key frames and setting up the timeline. You need to specify the node, duration for the animation, and end values that are interpolated. Special transition classes are available to combine multiple animations that may run sequentially or in parallel.

The Transition class contains an interpolator property that specifies the interpolator to be used during animation. By default, it uses Interpolator.EASE_BOTH, which starts the animation slowly, accelerates it, and slows it down toward the end.

Understanding the Fade Transition

An instance of the FadeTransition class represents a fade-in or fade-out effect for a node by gradually increasing or decreasing the opacity of the node over the specified duration. The class defines the following properties to specify the animation:
  • duration

  • node

  • fromValue

  • toValue

  • byValue

The duration property specifies the duration for one cycle of the animation.

The node property specifies the node whose opacity property is changed.

The fromValue property specifies the initial value for the opacity. If it is not specified, the current opacity of the node is used.

The toValue property specifies the opacity end value. The opacity of the node is updated between the initial value and the toValue for one cycle of the animation.

The byValue property lets you specify the opacity end value differently using the formula
opacity_end_value = opacity_initial_value + byValue

The byValue lets you set the opacity end value by incrementing or decrementing the initial value by an offset. If both toValue and byValue are specified, the toValue is used.

Suppose you want to set the initial and end opacity of a node between 1.0 and 0.5 in an animation. You can achieve it by setting the fromValue and toValue to 1.0 and 0.50 or by setting fromValue and byValue to 1.0 and –0.50.

The valid opacity value for a node is between 0.0 and 1.0. It is possible to set FadeTransition properties to exceed the range. The transition takes care of clamping the actual value in the range.

The following snippet of code sets up a fade-out animation for a Rectangle by changing its opacity from 1.0 to 0.20 in two seconds:
Rectangle rect = new Rectangle(200, 50, Color.RED);
FadeTransition fadeInOut = new FadeTransition(Duration.seconds(2), rect);
fadeInOut.setFromValue(1.0);
fadeInOut.setToValue(.20);
fadeInOut.play();
The program in Listing 19-4 creates a fade-out and fade-in effect in an infinite loop for a Rectangle .
// FadeTest.java
package com.jdojo.animation;
import javafx.animation.FadeTransition;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class FadeTest extends Application {
        public static void main(String[] args) {
                Application.launch(args);
        }
        @Override
        public void start(Stage stage) {
                Rectangle rect = new Rectangle(200, 50, Color.RED);
                HBox root = new HBox(rect);
                Scene scene = new Scene(root);
                stage.setScene(scene);
                stage.setTitle("Fade-in and Fade-out");
                stage.show();
                // Set up a fade-in and fade-out animation for the
                     // rectangle
                FadeTransition fadeInOut =
                         new FadeTransition(Duration.seconds(2), rect);
                fadeInOut.setFromValue(1.0);
                fadeInOut.setToValue(.20);
                fadeInOut.setCycleCount(FadeTransition.INDEFINITE);
                fadeInOut.setAutoReverse(true);
                fadeInOut.play();
        }
}
Listing 19-4

Creating a Fading Effect Using the FadeTransition Class

Understanding the Fill Transition

An instance of the FillTransition class represents a fill transition for a shape by gradually transitioning the fill property of the shape between the specified range and duration. The class defines the following properties to specify the animation:
  • duration

  • shape

  • fromValue

  • toValue

The duration property specifies the duration for one cycle of the animation.

The shape property specifies the Shape whose fill property is changed.

The fromValue property specifies the initial fill color. If it is not specified, the current fill of the shape is used.

The toValue property specifies the fill end value.

The fill of the shape is updated between the initial value and the toValue for one cycle of the animation. The fill property in the Shape class is defined as a Paint. However, the fromValue and toValue are of the type Color. That is, the fill transition works for two Colors, not two Paints.

The following snippet of code sets up a fill transition for a Rectangle by changing its fill from blue violet to azure in two seconds:
FillTransition fillTransition = new FillTransition(Duration.seconds(2), rect);
fillTransition.setFromValue(Color.BLUEVIOLET);
fillTransition.setToValue(Color.AZURE);
fillTransition.play();
The program in Listing 19-5 creates a fill transition to change the fill color of a Rectangle from blue violet to azure in two seconds in an infinite loop.
// FillTest.java
// ...find in the book's download area.
Listing 19-5

Creating a Fill Transition Using the FillTransition Class

Understanding the Stroke Transition

An instance of the StrokeTransition class represents a stroke transition for a shape by gradually transitioning the stroke property of the shape between the specified range and duration. The stroke transition works the same as the fill transition, except that it interpolates the stroke property of the shape rather than the fill property. The StrokeTransition class contains the same properties as the FillTransition class. Please refer to the section “Understanding the Fill Transition” for more details. The following snippet of code starts animating the stroke of a Rectangle in an infinite loop. The stroke changes from red to blue in a cycle duration of two seconds:
Rectangle rect = new Rectangle(200, 50, Color.WHITE);
StrokeTransition strokeTransition =
    new StrokeTransition(Duration.seconds(2), rect);
strokeTransition.setFromValue(Color.RED);
strokeTransition.setToValue(Color.BLUE);
strokeTransition.setCycleCount(StrokeTransition.INDEFINITE);
strokeTransition.setAutoReverse(true);
strokeTransition.play();

Understanding the Translate Transition

An instance of the TranslateTransition class represents a translate transition for a node by gradually changing the translateX, translateY, and translateZ properties of the node over the specified duration. The class defines the following properties to specify the animation:
  • duration

  • node

  • fromX

  • fromY

  • fromZ

  • toX

  • toY

  • toZ

  • byX

  • byY

  • byZ

The duration property specifies the duration for one cycle of the animation.

The node property specifies the node whose translateX, translateY, and translateZ properties are changed.

The initial location of the node is defined by the (fromX, fromY, fromZ) value. If it is not specified, the current (translateX, translateY, translateZ) value of the node is used as the initial location.

The (toX, toY, toZ) value specifies the end location.

The (byX, byY, byZ) value lets you specify the end location using the following formula:
translateX_end_value = translateX_initial_value + byX
translateY_end_value = translateY_initial_value + byY
translateZ_end_value = translateZ_initial_value + byZ

If both (toX, toY, toZ) and (byX, byY, byZ) values are specified, the former is used.

The program in Listing 19-6 creates a translate transition in an infinite loop for a Text object by scrolling it across the width of the scene. The program in Listing 19-2 created the same animation using a Timeline object with one difference. They use different interpolators. By default, timeline-based animations use the Interpolator.LINEAR interpolator, whereas transition-based animation uses the Interpolator.EASE_BOTH interpolator. When you run the program in Listing 19-6, the text starts scrolling slow in the beginning and end, whereas in Listing 19-2, the text scrolls with a uniform speed all the time.
// TranslateTest.java
// ...find in the book's download area.
Listing 19-6

Creating a Translate Transition Using the TranslateTransition Class

Understanding the Rotate Transition

An instance of the RotateTransition class represents a rotation transition for a node by gradually changing its rotate property over the specified duration. The rotation is performed around the center of the node along the specified axis. The class defines the following properties to specify the animation:
  • duration

  • node

  • axis

  • fromAngle

  • toAngle

  • byAngle

The duration property specifies the duration for one cycle of the animation.

The node property specifies the node whose rotate property is changed.

The axis property specifies the axis of rotation. If it is unspecified, the value for the rotationAxis property, which defaults to Rotate.Z_AXIS, for the node is used. The possible values are Rotate.X_AXIS, Rotate.Y_AXIS, and Rotate.Z_AXIS.

The initial angle for the rotation is specified by the fromAngle property. If it is unspecified, the value for the rotate property of the node is used as the initial angle.

The toAngle specifies the end rotation angle.

The byAngle lets you specify the end rotation angle using the following formula:
rotation_end_value = rotation_initial_value + byAngle

If both toAngle and byAngle values are specified, the former is used. All angles are specified in degrees. Zero degrees correspond to the 3 o’clock position. Positive values for angles are measured clockwise.

The program in Listing 19-7 creates a rotate transition in an infinite loop for a Rectangle. It rotates the Rectangle in clockwise and counterclockwise directions in alternate cycles.
// RotateTest.java
// ...find in the book's download area.
Listing 19-7

Creating a Rotate Transition Using the RotateTransition Class

Understanding the Scale Transition

An instance of the ScaleTransition class represents a scale transition for a node by gradually changing its scaleX, scaleY, and scaleZ properties over the specified duration. The class defines the following properties to specify the animation:
  • duration

  • node

  • fromX

  • fromY

  • fromZ

  • toX

  • toY

  • toZ

  • byX

  • byY

  • byZ

The duration property specifies the duration for one cycle of the animation.

The node property specifies the node whose scaleX, scaleY, and scaleZ properties are changed.

The initial scale of the node is defined by the (fromX, fromY, fromZ) value. If it is not specified, the current (scaleX, scaleY, scaleZ) value of the node is used as the initial scale.

The (toX, toY, toZ) value specifies the end scale.

The (byX, byY, byZ) value lets you specify the end scale using the following formula:
scaleX_end_value = scaleX_initial_value + byX
scaleY_end_value = scaleY_initial_value + byY
scaleZ_end_value = scaleZ_initial_value + byZ

If both (toX, toY, toZ) and (byX, byY, byZ) values are specified, the former is used.

The program in Listing 19-8 creates a scale transition in an infinite loop for a Rectangle by changing its width and height between 100% and 20% of their original values in two seconds.
// ScaleTest.java
// ...find in the book's download area.
Listing 19-8

Creating a Scale Transition Using the ScaleTransition Class

Understanding the Path Transition

An instance of the PathTransition class represents a path transition for a node by gradually changing its translateX and translateY properties to move it along a path over the specified duration. The path is defined by the outline of a Shape. The class defines the following properties to specify the animation:
  • duration

  • node

  • path

  • orientation

The duration property specifies the duration for one cycle of the animation.

The node property specifies the node whose rotate property is changed.

The path property defines the path along which the node is moved. It is a Shape. You can use an Arc, a Circle, a Rectangle, an Ellipse, a Path, a SVGPath, and so on as the path.

The moving node may maintain the same upright position, or it may be rotated to keep it perpendicular to the tangent of the path at any point along the path. The orientation property specifies the upright position of the node along the path. Its value is one of the constants (NONE and ORTHOGONAL_TO_TANGENT) of the PathTransition.OrientationType enum. The default is NONE, which maintains the same upright position. The ORTHOGONAL_TO_TANGENT value keeps the node perpendicular to the tangent of the path at any point. Figure 19-6 shows the positions of a Rectangle moving along a Circle using a PathTransition. Notice the way the Rectangle is rotated along the path when the ORTHOGONAL_TO_TANGENT orientation is used.
Figure 19-6

Effect of using the orientation property of the PathTransition class

You can specify the duration, path, and node for the path transition using the properties of the PathTransition class or in the constructors. The class contains the following constructors:
  • PathTransition()

  • PathTransition(Duration duration, Shape path)

  • PathTransition(Duration duration, Shape path, Node node)

The program in Listing 19-9 creates a path transition in an infinite loop for a Rectangle. It moves the Rectangle along a circular path defined by the outline of a Circle .
// PathTest.java
// ...find in the book's download area.
Listing 19-9

Creating a Path Transition Using the PathTransition Class

Understanding the Pause Transition

An instance of the PauseTransition class represents a pause transition. It causes a delay of the specified duration. Its use is not obvious. It is not used alone. Typically, it is used in a sequential transition to insert a pause between two transitions. It defines a duration property to specify the duration of the delay.

A pause transition is also useful if you want to execute an ActionEvent handler after a specified duration when a transition is finished. You can achieve this by setting its onFinished property, which is defined in the Animation class.
// Create a pause transition of 400 milliseconds that is the default duration
PauseTransition pt1 = new PauseTransition();
// Change the duration to 10 seconds
pt1.setDuration(Duration.seconds(10));
// Create a pause transition of 5 seconds
PauseTransition pt2 = new PauseTransition(Duration.seconds(5));

If you change the duration of a running pause transition, you need to stop and restart the transition to pick up the new duration. You will have an example when I discuss the sequential transition.

Understanding the Sequential Transition

An instance of the SequentialTransition class represents a sequential transition. It executes a list of animations in sequential order. The list of animations may contain timeline-based animations, transition-based animations, or both.

The SequentialTransition class contains a node property that is used as the node for animations in the list if the animation does not specify a node. If all animations specify a node, this property is not used.

A SequentialTransition maintains the animations in an ObservableList<Animation>. The getChildren() method returns the reference of the list.

The following snippet of code creates a fade transition, a pause transition, and a path transition. Three transitions are added to a sequential transition. When the sequential transition is played, it will play the fade transition, pause transition, and the path transition in sequence:
FadeTransition fadeTransition = ...
PauseTransition pauseTransition = ...
PathTransition pathTransition = ...
SequentialTransition st = new SequentialTransition();
st.getChildren().addAll(fadeTransition, pauseTransition, pathTransition);
st.play();
Tip

The SequentialTransition class contains constructors that let you specify the list of animations and node.

The program in Listing 19-10 creates a scale transition, a fill transition, a pause transition, and a path transition, which are added to a sequential transition. The sequential transition runs in an infinite loop. When the program runs
  • It scales up the rectangle to double its size, and then down to the original size.

  • It changes the fill color of the rectangle from red to blue and then back to red.

  • It pauses for 200 milliseconds and then prints a message on the standard output.

  • It moves the rectangle along the outline of a circle.

  • The foregoing sequence of animations is repeated indefinitely.

// SequentialTest.java
// ...find in the book's download area.
Listing 19-10

Creating a Sequential Transition Using the SequentialTransition Class

Understanding the Parallel Transition

An instance of the ParallelTransition class represents a parallel transition. It executes a list of animations simultaneously. The list of animations may contain timeline-based animations, transition-based animations, or both.

The ParallelTransition class contains a node property that is used as the node for animations in the list if the animation does not specify a node. If all animations specify a node, this property is not used.

A ParallelTransition maintains the animations in an ObservableList<Animation>. The getChildren() method returns the reference of the list.

The following snippet of code creates a fade transition and a path transition. The transitions are added to a parallel transition. When the sequential transition is played, it will apply the fading effect and move the node at the same time:
FadeTransition fadeTransition = ...
PathTransition pathTransition = ...
ParallelTransition pt = new ParallelTransition();
pt.getChildren().addAll(fadeTransition, pathTransition);
pt.play();
Tip

The ParallelTransition class contains constructors that let you specify the list of animations and node.

The program in Listing 19-11 creates a fade transition and a rotate transition. It adds them to a parallel transition. When the program is run, the rectangle rotates and fades in/out at the same time.
// ParallelTest.java
// ...find in the book's download area.
Listing 19-11

Creating a Parallel Transition Using the ParallelTransition Class

Understanding Interpolators

An interpolator is an instance of the abstract Interpolator class. An interpolator plays an important role in an animation. Its job is to compute the key values for the intermediate key frames during animation. Implementing a custom interpolator is easy. You need to subclass the Interpolator class and override its curve() method. The curve() method is passed the time elapsed for the current interval. The time is normalized between 0.0 and 1.0. The start and end of the interval have the value of 0.0 and 1.0, respectively. The value passed to the method would be 0.50 when half of the interval time has elapsed. The return value of the method indicates the fraction of change in the animated property.

The following interpolator is known as a linear interpolator whose curve() method returns the passed in argument value:
Interpolator linearInterpolator = new Interpolator() {
        @Override
        protected double curve(double timeFraction) {
                return timeFraction;
        }
};

The linear interpolator mandates that the percentage of change in the animated property is the same as the progression of the time for the interval.

Once you have a custom interpolator, you can use it in constructing key values for key frames in a timeline-based animation. For a transition-based animation, you can use it as the interpolator property of the transition classes.

The animation API calls the interpolate() method of the Interpolator. If the animated property is an instance of Number, it returns
startValue + (endValue - startValue) * curve(timeFraction)

Otherwise, if the animated property is an instance of the Interpolatable, it delegates the interpolation work to the interpolate() method of the Interpolatable. Otherwise, the interpolator defaults to a discrete interpolator by returning 1.0 when the time fraction is 1.0, and 0.0 otherwise.

JavaFX provides some standard interpolators that are commonly used in animations. They are available as constants in the Interpolator class or as its static methods:
  • Linear interpolator

  • Discrete interpolator

  • Ease-in interpolator

  • Ease-out interpolator

  • Ease-both interpolator

  • Spline interpolator

  • Tangent interpolator

Understanding the Linear Interpolator

The Interpolator.LINEAR constant represents a linear interpolator. It interpolates the value of the animated property of a node linearly with time. The percentage change in the property for an interval is the same as the percentage of the time passed.

Understanding the Discrete Interpolator

The Interpolator.DISCRETE constant represents a discrete interpolator. A discrete interpolator jumps from one key frame to the next, providing no intermediate key frame. The curve() method of the interpolator returns 1.0 when the time fraction is 1.0, and 0.0 otherwise. That is, the animated property value stays at its initial value for the entire duration of the interval. It jumps to the end value at the end of the interval. The program in Listing 19-12 uses discrete interpolators for all key frames. When you run the program, it moves text jumping from a key frame to another. Compare this example with the scrolling text example, which used a linear interpolator. The scrolling text example moved the text smoothly, whereas this example created a jerk in the movement.
// HoppingText.java
// ...find in the book's download area.
Listing 19-12

Using a Discrete Interpolator to Animate Hopping Text

Understanding the Ease-In Interpolator

The Interpolator.EASE_IN constant represents an ease-in interpolator. It starts the animation slowly for the first 20% of the time interval and accelerates afterward.

Understanding the Ease-Out Interpolator

The Interpolator.EASE_OUT constant represents an ease-out interpolator. It plays animation at a constant speed up to 80% of the time interval and slows down afterward.

Understanding the Ease-Both Interpolator

The Interpolator.EASE_BOTH constant represents an ease-both interpolator. Its plays the animation slower in the first 20% and the last 20% of the time interval and maintains a constant speed otherwise.

Understanding the Spline Interpolator

The Interpolator.SPLINE(double x1, double y1, double x2, double y2) static method returns a spline interpolator. It uses a cubic spline shape to compute the speed of the animation at any point in the interval. The parameters (x1, y1) and (x2, y2) define the control points of the cubic spline shape with (0, 0) and (1, 1) as implicit anchor points. The values of the parameters are between 0.0 and 1.0.

The slope at a given point on the cubic spline shape defines the acceleration at that point. A slope approaching the horizontal line indicates deceleration, whereas a slope approaching the vertical line indicates acceleration. For example, using (0, 0, 1, 1) as the parameters to the SPLINE method creates an interpolator with a constant speed, whereas the parameters (0.5, 0, 0.5, 1.0) will create an interpolator that accelerates in the first half and decelerates in the second half. Please refer to www.w3.org/TR/SMIL/smil-animation.html#animationNS-OverviewSpline for more details.

Understanding the Tangent Interpolator

The Interpolator.TANGENT static method returns a tangent interpolator, which defines the behavior of an animation before and after a key frame. All other interpolators interpolate data between two key frames. If you specify a tangent interpolator for a key frame, it is used to interpolate data before and after the key frame. The animation curve is defined in terms of a tangent, which is known as in-tangent, at a specified duration before the key frame and a tangent, which is called an out-tangent, at a specified duration after the key frame. This interpolator is used only in timeline-based animations as it affects two intervals.

The TANGENT static method is overloaded:
  • Interpolator TANGENT(Duration t1, double v1, Duration t2, double v2)

  • Interpolator TANGENT(Duration t, double v)

In the first version, the parameters t1 and t2 are the duration before and after the key frame, respectively. The parameters v1 and v2 are the in-tangent and out-tangent values. That is, v1 is the tangent value at duration t1, and v2 is the tangent value at duration t2. The second version specifies the same value for both pairs.

Summary

In JavaFX, animation is defined as changing the property of a node over time. If the property that changes determines the location of the node, the animation in JavaFX will produce an illusion of motion. Not all animations have to involve motion; for example, changing the fill property of a Shape over time is an animation in JavaFX that does not involve motion.

Animation is performed over a period of time. A timeline denotes the progression of time during animation with an associated key frame at a given instant. A key frame represents the state of the node being animated at a specific instant on the timeline. A key frame has associated key values. A key value represents the value of a property of the node along with an interpolator to be used.

A timeline animation is used for animating any properties of a node. An instance of the Timeline class represents a timeline animation. Using a timeline animation involves the following steps: constructing key frames, creating a Timeline object with key frames, setting the animation properties, and using the play() method to run the animation. You can add key frames to a Timeline at the time of creating it or after. The Timeline instance keeps all key frames in an ObservableList<KeyFrame> object. The getKeyFrames() method returns the list. You can modify the list of key frames at any time. If the timeline animation is already running, you need to stop and restart it to pick up the modified list of key frames.

The Animation class contains several properties and methods to control animation such as playing, reversing, pausing, and stopping.

You can set up cue points on a timeline. Cue points are named instants on the timeline. An animation can jump to a cue point using the jumpTo(String cuePoint) method.

Using timeline animation is not easy in all cases. JavaFX contains a number of classes (known as transitions) that let you animate nodes using predefined properties. All transition classes inherit from the Transition class, which, in turn, inherits from the Animation class. The transition classes take care of creating the key frames and setting up the timeline. You need to specify the node, duration for the animation, and end values that are interpolated. Special transition classes are available to combine multiple animations that may run sequentially or in parallel. The Transition class contains an interpolator property that specifies the interpolator to be used during animation. By default, it uses Interpolator.EASE_BOTH, which starts the animation slowly, accelerates it, and slows it down toward the end.

An interpolator is an instance of the abstract Interpolator class. Its job is to compute the key values for the intermediate key frames during animation. JavaFX provides several built-in interpolators such as linear, discrete, ease-in, and ease-out. You can also implement a custom interpolator easily. You need to subclass the Interpolator class and override its curve() method. The curve() method is passed the time elapsed for the current interval. The time is normalized between 0.0 and 1.0. The return value of the method indicates the fraction of change in the animated property.

The next chapter will discuss how to incorporate different types of charts in a JavaFX application.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.9.138