Lesson 7: Creating Animated Objects
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 theStage
,Scene
, andColor
classes. - Add the
Stage
object literal and define thetitle
instance variable. - Add the
Scene
object literal to thescene
instance variable of theStage
class. - Define the color of the scene using the
fill
variable of theScene
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
andImageView
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 thesun
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; |
When compiled and run on the mobile emulator, this modified code produces the following window.
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.
To draw this cloud in your code you need to perform the following steps:
- Add
import
statements for theMoveTo
,ArcTo
,Path
,LinearGradient
, andStop
classes. Refer to Creating Graphical Objects for more information about shapes and filling methods. - Use the
MoveTo
,ArcTo
, andPath
classes from thejavafx.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
TheMoveTo
class indicates the start point for the shape, and theArcTo
class creates an arc segment. All segments are combined into a shape using thePath
class which represents a simple shape, and enables basic construction of a geometric path. ThePath
class is helpful when you need to create your own shape that is different from the primitive graphic shapes available in thejavafx.scene.shape
package. ThePath
class extends theNode
class and inherits all of its instance variables and functions.
Note: ThesweepFlag
instance variable is set totrue
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; |
When compiled and run in a desktop window, this modified code produces the following window.
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 could 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 thePath
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.
Figure 4: Key Frames
Animation occurs along a timeline, represented by ajavafx.animation.Timeline
object. Each timeline contains two or more key frames, represented byjavafx.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();
Thetime
instance variable defines the elapsed time at which the associated values will be set within a single cycle of theTimeline
object. Thetime
is a variable of thejavafx.lang.Duration
class that takes aNumber
value followed by "s" or "ms" to indicate seconds or milliseconds. The=>
operator provides a literal constructor for a list of key values. Thetween
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.
AlthoughKeyFrame
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 byat
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 thePath
object to thex
variable as shown in the following code fragment:
Path{
...
translateX: bind x
...
}
When thex
variable changes, thetranslateX
variable of thePath
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 toTimeline.INDEFINITE
to loop the animation. - Set the
autoReverse
instance variable totrue
to enable two-way movement.
The following code accomplishes these tasks:
import javafx.animation.Timeline; |
The modified code of the application is shown below:
import javafx.animation.Interpolator; |
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 they
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: They
variable attains its maximum position after seven seconds, which is faster than thex
variable. Therefore, thetranslateY
value changes faster thantranslateX
. 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
Desktop Profile
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
andjavafx.scene.effect.light.DistantLight
classes - Apply the following code for the
effect
instance variable of thePath
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.
0 comments:
Post a Comment