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.