The JavaFX Script programming language supports key frame animation, a declarative model in which the programmer describes the animated state transitions of each "scene" by declaring "snapshots" (key frames) of state at certain points in time. The JavaFX Script programming language supports two basic varieties of key frame animation: discrete and interpolated. The difference is that for the latter, special interpolation functions calculate that states that occur between each animation frame. In either case, the system will automatically perform the animation, stop it, pause it, resume it, reverse it, or repeat it as requested.
At a high level, key frame animation can be described as follows:
javafx.animation.Timeline object.
javafx.animation.KeyFrame objects.
autoReverse, repeatCount,
toggle, etc.) plus a number of functions that control its
playback (start(), stop(),
pause(), and resume()).javafx.animation.KeyValue object describes an
end-state of a property at the time of the key frame together with a
function which is used to calculate the "in-between" values relative
to the previous key frame for that property.
Note: Timeline objects can also be used for general-purpose time-based operations that are independent of graphics animation.
In a discrete animation, the value of a given property instantaneously transitions to the value given in the key frame at the time instant of the key frame. For example, consider the classic "tumbling duke" animation, where the illusion of motion comes from simply flipping through a series of images. The animation is "discrete" because there is nothing to calculate "between" each animation frame.
import javafx.application.Frame;
import javafx.application.Stage;
import javafx.scene.paint.Color;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
// Load image data
var images = for (i in [1..17]) {
Image { url: "file:images/T{i}.gif"}; // assumes images are stored locally
// To load images from the web, use this line instead:
// Image { url: "http://java.sun.com/applets/other/TumblingDuke/images/T{i}.gif"};
}
// Set first image
var currDuke = images[0];
// Create timeline to cycle images
var timeline = Timeline {
toggle: true
keyFrames: for (image in images) {
KeyFrame {
time: 100ms* indexof image // each frame is 100ms apart
action: function(){currDuke=image;}
}
}
}
Frame {
title: "Click on Duke!"
width: 250
height: 150
visible: true
stage: Stage {
content: ImageView {
image: bind currDuke;
onMouseClicked: function(e) {timeline.start();}
}
}
}
First, this program loads the images into memory and creates a
variable (currDuke) to represent the current image. Next, it
creates a timeline and populates it with a number of key frames (one per
image.) Each key frame is placed 100ms apart and assigns its image to
currDuke. Finally, the GUI binds the image that it displays
to currDuke, ensuring that the correct image will be
displayed on screen as the application cycles through its animation
frames.
Some animations, however, have states that must be dynamically calculated. The next example spins a 2D rectangle as it changes size and cycles through a range of colors. This animation is considered interpolated because instead of creating key frames for every possible position along the animation path, the programmer simply declares a few key frames (start, mid, end) and lets the system calculate everything in between.
import javafx.application.Frame;
import javafx.application.Stage;
import javafx.scene.geometry.Rectangle;
import javafx.scene.paint.Color;
import javafx.scene.transform.Transform;
import javafx.scene.Cursor;
import javafx.animation.Timeline;
import javafx.animation.Interpolator;
Frame {
title: "Animation Demo";
width: 500
height: 400
visible: true
stage: Stage {
content: Rectangle {
// Declare variables
var rotation = 0.0;
var size = 1.0;
var color = Color.GREEN;
// Initialize attributes
cursor: Cursor.HAND
height: 50
width: 50
fill: bind color
transform: bind [Transform.translate(100,100),
Transform.scale(size,size),
Transform.rotate(rotation,25,25)]
// Create animation timeline
var myTimeline = Timeline {
toggle: true
// Key Frames
var begin = at (0s) {
size => 1.0;
color => Color.GREEN;
rotation => 0.0;
}
var mid = at (0.5s) {
color => Color.PURPLE tween Interpolator.EASEBOTH;
}
var end = at (1s) {
size => 3.0 tween Interpolator.LINEAR;
color => Color.RED tween Interpolator.EASEBOTH;
rotation => 360.0 tween Interpolator.EASEBOTH;
}
keyFrames: [begin,mid,end]
}
onMouseClicked: function(e) {
myTimeline.start();
}
}
}
}
Note that although key frame animations are created from normal
objects, special syntax is provided to make the task easier, namely the
=>, tween, and at operators. In
this example, we have created the key frames using the "at"
operator, which is a literal constructor for KeyFrame
objects.