Animation, the Easy Way!

If you've ever programmed animations and thought "there must be an easier way", then today's blog entry is sure to bring welcomed relief. The JavaFXTM Script programming language now supports key frame animation, which allows you to declare your animations using a simple syntax that resembles plain English. All graphics rendering now happens automatically, behind the scenes, allowing you to focus on the animation at hand rather than the "plumbing" to make it all work.

To demonstrate this functionality we will re-create the classic "tumbling duke" animation. Since this may be your first exposure to the language, we'll break this discussion up into two separate entries: today we will cover the basics; early next week we'll cover everything else. You'll want to try this code for yourself, but fear not, the development environment is easy to set up. The edit-compile-run process is similar to that of the JavaTM programming language, but the commands are javafxc and javafx instead of javac and java. There is also a JavaFX Script compiler plug-in available for the NetBeans IDE, for those who prefer not to work from the command line. For instructions on setting up any of these environments, visit the openjfx project page at http://openjfx.dev.java.net.

Having said that, let's get animating!

As you may already know, the tumbling duke animation is comprised of 17 separate images. Traditionally, creating this animation has required an intimate understanding of the AWT and Swing painting mechanisms (if you haven't seen it before, take a look at the original Tumbling Duke source code.) As you will soon learn, accomplishing the same end result using JavaFX Script technology is a simple matter once you've mastered its syntax.

import javafx.ui.\*;
import javafx.ui.canvas.\*;
import javafx.animation.\*;

// Load image data
var images = for (i in [1..17]) {
     Image { url: "file:images/T{i}.gif"};
}

...


Listing 1: Importing Classes and Loading Images

Our first task is to load the images into memory. If your initial instinct is to place these images into an array, you're right, although here that data structure is known as a sequence. Sequences differ from Java programming language arrays, but are similar in that they also hold a list of objects. Sequences are generally declared with square brackets (the code [1..17] is a sequence), but the system will also infer this based on context when needed. Such is the case with our var images declaration. The for loop that follows returns one javafx.ui.Image object per iteration; when it's done, you end up with an images variable that contains all of the image data. (If you're wondering where the constructor call is, the JavaFX Script programming language uses object literals; the code Image{} is enough to return a new Image object.)

...

// Create timeline and key animation frames
var t = Timeline {
     keyFrames: for (image in images) {
          KeyFrame {
               time: 100ms\* indexof image
               action: function(){currDuke=image;}
          }
     }
}

...

Listing 2: Creating the Key Frames and Animation Timeline

Next, we'll create the animation's key frames and place them on a timeline. These constructs are represented by the javafx.animation.KeyFrame and javafx.animation.Timeline classes, respectively. In the interest of saving space we've rolled a lot of functionality into the above code, so let's take a minute to make sure you understand what's going on.

In the JavaFX Script programming language, an object's state is stored in its attributes. Above we initialize the keyFrames attribute of the Timeline class, followed by the time and action attributes of the KeyFrame class. So far as the animation itself is concerned, the important concepts are that a "key frame" (a KeyFrame instance) represents one specific point in time along your animation, and a Timeline instance holds a sequence of KeyFrame objects. It's up to you to decide where the key frames of your animation should be; in this case we consider each flip of the image to be one frame in the animation. It seems reasonable that the images should change 100 milliseconds apart, so we've initialized each key frame with a time value that is 100ms greater than the previous frame.

The action attribute of the KeyFrame class accepts a function; that function can contain any code that you want. Keep in mind that we're looping through each of the images where this occurs, so the only action that we care about is assigning the current image of the current key frame to a separate variable named currDuke. As you will see below, this currDuke variable is where the GUI will look to find the current image.

 ... 

// Set first image to be displayed
var currDuke = images[0];

// Create the GUI application frame
Frame {
     title: "Tumbling Duke"
     width: 400
     height: 150
     background: Color.WHITE
     visible: true
     content: FlowPanel {
          content: Canvas {
               onMouseClicked: function(e) {t.start();}
               content: ImageView {
                    image: bind currDuke;
               }
          }
     }
}


Listing 3: Creating the Application GUI

At this point, all that's left to do is to construct the GUI. This is actually the easiest part; all we need to do is create a few objects and we're done. The image attribute of the ImageView class is the actual image that will appear on screen. The code "image: bind currDuke" is the magic that keeps the GUI in sync with the images used in our timeline. By "binding" the image attribute to the currDuke variable, the on-screen image will automatically change whenever the value of currDuke changes.

In this early version of the code, we'll tell the animation timeline to begin only when the user clicks somewhere on the GUI's drawing surface. In the next blog entry we'll change that behavior to an infinite loop that begins automatically, thereby replicating the behavior of the original animation.

import javafx.ui.\*;
import javafx.ui.canvas.\*;
import javafx.animation.\*;

// Load image data
var images = for (i in [1..17]) {
     Image { url: "file:images/T{i}.gif"};
}

// Create timeline and key animation frames
var t = Timeline {
     keyFrames: for (image in images) {
          KeyFrame {
               time: 100ms\* indexof image
               action: function(){currDuke=image;}
          }
     }
}

// Set first image
var currDuke = images[0];

// Create the GUI application frame
Frame {
     title: "Tumbling Duke"
     width: 400 
     height: 150
     background: Color.WHITE
     visible: true
     content: FlowPanel {
          content: Canvas {
               onMouseClicked: function(e) {t.start();}
               content: ImageView {
                    image: bind currDuke;
               }
          }
     }
}

Listing 4: The Complete Code Listing

The listing above shows the complete source code so far. Be sure to check back next week for the continuation of this discussion!

-- Scott Hommel

Comments:

Post a Comment:
  • HTML Syntax: NOT allowed
About

Blog about Java technology documentation and news about Java releases.

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today