This lesson is divided into two sections. The common profile includes actions for using animation both for mobile and desktop applications. The second section adds some visual effects to run the application in the desktop window.
This section introduces a step-by-step procedure that demonstrates how to add animation to a simple application. It will guide you through the creation of a cloud that travels across a sky of sunshine, and bounces off the window borders, as displayed in the following window.
As you learned in Presenting UI Objects in a Graphical Scene, UI components, shapes, text, and images are considered a hierarchy of objects in a graphical scene. Animation of these graphical objects also takes place in scene, so the first step is to create a scene.
- Add
import statements for the Stage, Scene, and Color classes.
- Add the
Stage object literal and define the title instance variable.
- Add the
Scene object literal to the scene instance variable of the Stage class.
- Define the color of the scene using the
fill variable of the Scene class.
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
Stage{
title: "Cloud"
scene:
Scene{
fill: Color.WHITE
}//Scene
}//Stage
|
Refer to Using Declarative Syntax for more information about declarative syntax employed in the JavaFX Script programming language.
In the JavaFX SDK, images are created using the javafx.scene.image.Image class, where the image location is specified in the url instance variable. Note that only a Node object can be added to a scene's content, so you need to use an adapter class, called ImageView. More details about scene and nodes are in Presenting UI Objects in a Graphical Scene.
- Add two new imports for the
Image and ImageView classes.
- Set an image that will serve as a background for the traveling cloud. Use the sun.jpg image.
When specifying the image url, you can set the URI of the resource or use the relative codebase. In this example the image url is set using the
__DIR__ variable that indicates the directory where the image is located. By default it points to the current directory, so make sure that the sun image is located in the same directory as application compiled classes. To run the application on the mobile emulator make sure that the image is packed into the application jar file along with the compiled classes.
These changes are reflected in the modified code shown below:
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
Stage{
title: "Cloud"
scene:
Scene{
fill: Color.WHITE
content:[
ImageView{
image: Image{
url: "{__DIR__}sun.jpg"
}//Image
}//ImageView
]
}//Scene
}//Stage
|
When compiled and run on the mobile emulator, this modified code produces the following window.
Figure 1: A scene with an image as background
Create the actual cloud by drawing five successive arcs, joining the last one to the first. The end point of the first arc is the start point of the second arc. This is illustrated in the following diagram.
Figure 2: End-points of arcs that form cloud
To draw this cloud in your code you need to perform the following steps:
- Add
import statements for the MoveTo, ArcTo, Path, LinearGradient, and Stop classes. Refer to Creating Graphical Objects for more information about shapes and filling methods.
- Use the
MoveTo, ArcTo, and Path classes from the javafx.scene.shape package as shown in the following code fragment.
Path {
stroke: Color.DODGERBLUE
fill: LinearGradient {
startX:60,
startY:10,
endX:10
endY:80 ,
proportional: false
stops: [
Stop {offset: 0.0 color: Color.DODGERBLUE},
Stop {offset: 0.5 color: Color.LIGHTSKYBLUE},
Stop {offset: 1.0 color: Color.WHITE}
]
}
elements: [
MoveTo {x: 15 y: 15 },
ArcTo {x: 50 y: 10 radiusX: 20 radiusY: 20 sweepFlag: true},
ArcTo {x: 70 y: 20 radiusX: 20 radiusY: 20 sweepFlag: true},
ArcTo {x: 50 y: 60 radiusX: 20 radiusY: 20 sweepFlag: true},
ArcTo {x: 20 y: 50 radiusX: 10 radiusY: 5 sweepFlag: true},
ArcTo {x: 15 y: 15 radiusX: 10 radiusY: 10 sweepFlag: true},
]
}//Path
|
The MoveTo class indicates the start point for the shape, and the ArcTo class creates an arc segment. All segments are combined into a shape using the Path class which represents a simple shape, and enables basic construction of a geometric path. The Path class is helpful when you need to create your own shape that is different from the primitive graphic shapes available in the javafx.scene.shape package. The Path class extends the Node class and inherits all of its instance variables and functions.
Note: The sweepFlag instance variable is set to true so that the arc be drawn clockwise, in a "positive" angle. If the arcs are drawn counterclockwise, they will not curve correctly.
The following modified code includes a graphical scene, an image, and a cloud:
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.shape.ArcTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
Stage{
title: "Cloud"
scene: Scene{
fill: Color.WHITE
content:[
ImageView{
image: Image{
url: "{__DIR__}sun.jpg"
}
},//ImageView
Path {
stroke: Color.DODGERBLUE
fill: LinearGradient {
startX:60,
startY:10,
endX:10
endY:80 ,
proportional: false
stops: [
Stop {offset: 0.0 color: Color.DODGERBLUE},
Stop {offset: 0.5 color: Color.LIGHTSKYBLUE},
Stop {offset: 1.0 color: Color.WHITE}
]
}//LinearGradient
elements: [
MoveTo {x: 15 y: 15 },
ArcTo {x: 50 y: 10 radiusX: 20 radiusY: 20 sweepFlag: true},
ArcTo {x: 70 y: 20 radiusX: 20 radiusY: 20 sweepFlag: true},
ArcTo {x: 50 y: 60 radiusX: 20 radiusY: 20 sweepFlag: true},
ArcTo {x: 20 y: 50 radiusX: 10 radiusY: 5 sweepFlag: true},
ArcTo {x: 15 y: 15 radiusX: 10 radiusY: 10 sweepFlag: true},
]
}//Path
]
}//Scene
}//Stage
|
When compiled and run in a desktop window, this modified code produces the following window.
Figure 3: A window with an image and a cloud-looking shape
The next step is to animate the cloud. The JavaFX Script Language supports the Key Frame animation concept. This means that the animated state transitions of the graphical scene are declared by start and end snapshots (key frames) of the scene's state at certain points in time. Given these two states, the system can automatically perform the animation. It can stop, pause, resume, reverse or repeat movement when requested.
First, simplify the task by animating the cloud horizontally, with no vertical motion. Later you will add in the vertical motion. To animate the cloud horizontally, alter the translateX instance variable of the Path object, and leave the translateY instance variable constant. Perform the following steps:
- Set the
translateY variable of the Path object to 100.
- Define key frames for the start point (0, 100) and the end point (158, 100). To calculate these values, take into consideration the image size, which is 241x332, and the shape size, which is 83x64. These measurements are illustrated in the following diagram.
Animation occurs along a timeline, represented by a javafx.animation.Timeline object. Each timeline contains two or more key frames, represented by javafx.animation.KeyFrame objects.
- Create a timeline with two key frames to perform the cloud's horizontal movement and starts the movement when the application is launched. Positions between the start and the end points are calculated using linear interpolation.
import javafx.animation.Timeline;
import javafx.animation.KeyFrame;
import javafx.animation.Interpolator;
var x: Number;
Timeline {
keyFrames: [
KeyFrame{
time: 0s
values: x => 0.0
},
KeyFrame{
time: 4s
values: x => 158.0 tween Interpolator.LINEAR
}
]
}.play();
|
The time instance variable defines the elapsed time at which the associated values will be set within a single cycle of the Timeline object. The time is a variable of the javafx.lang.Duration class that takes a Number value followed by "s" or "ms" to indicate seconds or milliseconds. The => operator provides a literal constructor for a list of key values. The tween operator is a literal constructor for an interpolated value. Therefore the cloud begins at pixel 0 and moves to position 158 over the course of four seconds.
Although KeyFrame animations are typical JavaFX objects, special syntax is provided to make it easier to express animation than is possible with the standard object-literal syntax. The trigger clause enables you to associate an arbitrary callback with the key frame. The time specified by at is relative to the start of the timeline. This capability simplifies the code as follows:
import javafx.animation.Timeline;
import javafx.animation.Interpolator;
var x: Number;
Timeline {
keyFrames: [
at (0s) {x => 0.0},
at (4s) {x => 158.0 tween Interpolator.LINEAR}
]
}.play();
|
- Bind the
translateX instance variable of the Path object to the x variable as shown in the following code fragment:
Path{
...
translateX: bind x
...
}
|
When the x variable changes, the translateX variable of the Path object also changes. More details about the Data Binding mechanism are in Applying Data Binding to UI Objects.
You can use instance variables of the Timeline class to control the timeline cycle.
- Set the
repeatCount instance variable to Timeline.INDEFINITE to loop the animation.
- Set the
autoReverse instance variable to true to enable two-way movement.
The following code accomplishes these tasks:
import javafx.animation.Timeline;
import javafx.animation.Interpolator;
var x: Number;
Timeline {
repeatCount: Timeline.INDEFINITE
autoReverse: true
keyFrames: [
at (0s) {x => 0.0},
at (4s) {x => 158.0 tween Interpolator.LINEAR}
]
}.play();
|
The modified code of the application is shown below:
import javafx.animation.Interpolator;
import javafx.animation.Timeline;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.shape.ArcTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
var x: Number;
Timeline {
repeatCount: Timeline.INDEFINITE
autoReverse: true
keyFrames: [
at (0s) {x => 0.0},
at (4s) {x => 158.0 tween Interpolator.LINEAR}
]
}.play();
Stage{
title: "Cloud"
scene: Scene{
fill: Color.WHITE
content:[
ImageView{
image: Image{
url: "{__DIR__}sun.jpg"
}},
Path {
translateX: bind x
translateY: 100
stroke: Color.DODGERBLUE
fill: LinearGradient {
startX:60,
startY:10,
endX:10
endY:80 ,
proportional: false
stops: [
Stop {offset: 0.0 color: Color.DODGERBLUE},
Stop {offset: 0.5 color: Color.LIGHTSKYBLUE},
Stop {offset: 1.0 color: Color.WHITE}
]
}
elements: [
MoveTo {x: 15 y: 15 },
ArcTo {x: 50 y: 10 radiusX: 20 radiusY: 20 sweepFlag: true},
ArcTo {x: 70 y: 20 radiusX: 20 radiusY: 20 sweepFlag: true},
ArcTo {x: 50 y: 60 radiusX: 20 radiusY: 20 sweepFlag: true},
ArcTo {x: 20 y: 50 radiusX: 10 radiusY: 5 sweepFlag: true},
ArcTo {x: 15 y: 15 radiusX: 10 radiusY: 10 sweepFlag: true},
]
}//Path
]
}//Scene
}//Stage
|
When compiled and run this code produces the following window:
This animation application uses linear interpolation which moves the object in even time increments. You can play with other forms of interpolation. For example, if you set the Interpolator.EASEBOTH type, the cloud will slightly slow down at the start and at the end of the timeline cycle.
You can enhance the application by creating the originally desired floating effect.
- Create another timeline for the
y coordinate of the shape.
- Bind the
translateY instance variable to the y value as shown on the following code fragment:
var y: Number;
Timeline {
repeatCount: Timeline.INDEFINITE
autoReverse: true
keyFrames: [
at (0s) {y => 0.0},
at (7s) {y => 258.0 tween Interpolator.LINEAR},
]
}.play();
...
Path{
...
translateY: bind y
...
}//Path
|
Note: The y variable attains its maximum position after seven seconds, which is faster than the x variable. Therefore, the translateY value changes faster than translateX. This produces a wandering effect.
The following is the complete code of the example.
import javafx.animation.Interpolator;
import javafx.animation.Timeline;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.shape.ArcTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
var x: Number;
Timeline {
repeatCount: Timeline.INDEFINITE
autoReverse: true
keyFrames: [
at (0s) {x => 0.0},
at (4s) {x => 158.0 tween Interpolator.LINEAR}
]
}.play();
var y: Number;
Timeline {
repeatCount: Timeline.INDEFINITE
autoReverse: true
keyFrames: [
at (0s) {y => 0.0},
at (7s) {y => 258.0 tween Interpolator.LINEAR},
]
}.play();
Stage{
title: "Cloud"
scene: Scene{
fill: Color.WHITE
content:[
ImageView{
image: Image{
url: "{__DIR__}sun.jpg"
}},
Path {
translateX: bind x
translateY: bind y
stroke: Color.DODGERBLUE
fill: LinearGradient {
startX:60,
startY:10,
endX:10
endY:80 ,
proportional: false
stops: [
Stop {offset: 0.0 color: Color.DODGERBLUE},
Stop {offset: 0.5 color: Color.LIGHTSKYBLUE},
Stop {offset: 1.0 color: Color.WHITE}
]
}
elements: [
MoveTo {x: 15 y: 15 },
ArcTo {x: 50 y: 10 radiusX: 20 radiusY: 20 sweepFlag: true},
ArcTo {x: 70 y: 20 radiusX: 20 radiusY: 20 sweepFlag: true},
ArcTo {x: 50 y: 60 radiusX: 20 radiusY: 20 sweepFlag: true},
ArcTo {x: 20 y: 50 radiusX: 10 radiusY: 5 sweepFlag: true},
ArcTo {x: 15 y: 15 radiusX: 10 radiusY: 10 sweepFlag: true},
]
}//Path
]
}//Scene
}//Stage
|
When compiled and run, this code produces the following window.
Figure 5: The application run on the mobile emulator
You can apply one of the effects available in the javafx.scene.effect and javafx.scene.effect.light packages to visually enhance the application. These packages exist only in the desktop profile. Perform the following steps to create the lighting effect and make the cloud seem embossed.
- Add import statements for the
javafx.scene.effect.Lighting and javafx.scene.effect.light.DistantLight classes
- Apply the following code for the
effect instance variable of the Path object.
effect: Lighting{light: DistantLight{azimuth: 90}}
|
This effect simulates lighting up the object with a distant light source. The azimuth instance variable defines the angle of the light source.
The complete code of the desktop-specific application is located in the cloudDesktop.fx file. When compiled and run this code produces the window.
Figure 6: Desktop-specific application
Conclusion
This lesson described how to create an animated object and examined interpolated animation. Try the concepts and techniques mentioned in this lesson to explore the other animation capabilities of JavaFX SDK.
|
Alla Redko is a technical writer at Sun. She develops documentation for Java SE and JavaFX. Prior to her assignment to Sun, she worked as a technical writer for 10 years.
|