Chapter 10. Animation

Animation Overview

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:

  • Animations occur along a timeline, represented by a javafx.animation.Timeline object.
  • Each timeline contains two or more key frames, represented by javafx.animation.KeyFrame objects.
  • Each timeline also provides certain attributes that describe it (autoReverse, repeatCount, toggle, etc.) plus a number of functions that control its playback (start(), stop(), pause(), and resume()).
  • Each key frame is associated with a time instant relative to its containing timeline and itself contain 3 things: a list of key values, a list of sub-timelines, and a "trigger". Each contained sub-timeline is evaluated with its starting point relative to the key frame's key time. Timelines can therefore be constructed hierarchically and contain sub-timelines which may themselves repeat. The "trigger", if present, is a block of procedural code which is executed at the time instant specified by the key frame.
  • A 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.

Discrete 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.

Interpolated Animation

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.