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
importstatements for theStage,Scene, andColorclasses. - Add the
Stageobject literal and define thetitleinstance variable. - Add the
Sceneobject literal to thesceneinstance variable of theStageclass. - Define the color of the scene using the
fillvariable of theSceneclass.
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
ImageandImageViewclasses. - 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 thesunimage 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
importstatements for theMoveTo,ArcTo,Path,LinearGradient, andStopclasses. Refer to Creating Graphical Objects for more information about shapes and filling methods. - Use the
MoveTo,ArcTo, andPathclasses from thejavafx.scene.shapepackage 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
TheMoveToclass indicates the start point for the shape, and theArcToclass creates an arc segment. All segments are combined into a shape using thePathclass which represents a simple shape, and enables basic construction of a geometric path. ThePathclass is helpful when you need to create your own shape that is different from the primitive graphic shapes available in thejavafx.scene.shapepackage. ThePathclass extends theNodeclass and inherits all of its instance variables and functions.
Note: ThesweepFlaginstance variable is set totrueso 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
translateYvariable of thePathobject 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.Timelineobject. Each timeline contains two or more key frames, represented byjavafx.animation.KeyFrameobjects. - 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();
Thetimeinstance variable defines the elapsed time at which the associated values will be set within a single cycle of theTimelineobject. Thetimeis a variable of thejavafx.lang.Durationclass that takes aNumbervalue followed by "s" or "ms" to indicate seconds or milliseconds. The=>operator provides a literal constructor for a list of key values. Thetweenoperator 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.
AlthoughKeyFrameanimations 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 byatis 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
translateXinstance variable of thePathobject to thexvariable as shown in the following code fragment:
Path{
...
translateX: bind x
...
}
When thexvariable changes, thetranslateXvariable of thePathobject 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
repeatCountinstance variable toTimeline.INDEFINITEto loop the animation. - Set the
autoReverseinstance variable totrueto 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
ycoordinate of the shape. - Bind the
translateYinstance variable to theyvalue 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: Theyvariable attains its maximum position after seven seconds, which is faster than thexvariable. Therefore, thetranslateYvalue 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.Lightingandjavafx.scene.effect.light.DistantLightclasses - Apply the following code for the
effectinstance variable of thePathobject.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