在本章中,我们将使用 JavaFX 中的动画。 我们使用AnimationTimer
,Transition
和Timeline
创建动画。
动画是连续的图像,使人产生了运动的幻觉。 但是,动画不仅限于运动。 随着时间的推移更改节点的背景也被视为动画。
JavaFX 提供了三种创建动画的基本工具:
- 动画计时器
- 过渡
- 时间线
AnimationTimer
是创建动画的最简单工具。 这是一个基本计时器。 在动画的每一帧中都会调用其handle()
方法。 Transition
是定义动画的基本高级框架。 动画由interpolate()
方法的frac
值控制。 Timeline
是用于制作高级动画的最复杂的工具。 Timeline
动画由KeyFrames
定义,该动画概述了沿Timeline
插补的一组变量在指定时间点的节点目标值。 动画属性由KeyValues
定义。
动画类
Animation
是 JavaFX 中定义高级动画的基本类。 Transition
和Timeline
都扩展了Animation
。 动画以play()
或playFromStart()
方法开始,以stop()
方法结束。 可以通过调用pause()
方法暂停动画,然后下一个play()
调用从暂停的位置继续播放动画。 rate
属性定义了预期播放动画的方向和速度。 delay
属性指定动画的初始延迟量。 动画可以循环运行; cycleCount
属性中定义了循环数,cycleDuration
指示了循环的持续时间。 可以使用autoReverseProperty
以交替的周期反转动画。
动画计时器
AnimationTimer
允许创建一个定时器,该定时器在活动时在每个帧中被调用。 这是一个抽象类; 因此,我们需要创建一个扩展它的自定义类。 在每个帧中调用的handle()
方法都必须重写。 AnimationTimer
的start()
方法启动计时器,stop()
方法停止计时器。
AnimationTimerEx.java
package com.zetcode;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Font;
import javafx.stage.Stage;
/**
* ZetCode JavaFX tutorial
*
* This program uses AnimationTimer to
* create an animation.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class AnimationTimerEx extends Application {
private double opacity = 1;
private Label lbl;
@Override
public void start(Stage stage) {
initUI(stage);
}
private void initUI(Stage stage) {
StackPane root = new StackPane();
lbl = new Label("JavaFX");
lbl.setFont(Font.font(48));
root.getChildren().add(lbl);
AnimationTimer timer = new MyTimer();
timer.start();
Scene scene = new Scene(root, 300, 250);
stage.setTitle("AnimationTimer");
stage.setScene(scene);
stage.show();
}
private class MyTimer extends AnimationTimer {
@Override
public void handle(long now) {
doHandle();
}
private void doHandle() {
opacity -= 0.01;
lbl.opacityProperty().set(opacity);
if (opacity <= 0) {
stop();
System.out.println("Animation stopped");
}
}
}
public static void main(String[] args) {
launch(args);
}
}
该示例使用AnimationTimer
在节点上创建淡出效果。
lbl = new Label("JavaFX");
lbl.setFont(Font.font(48));
root.getChildren().add(lbl);
我们的动画更改了此Label
控件的属性。
AnimationTimer timer = new MyTimer();
timer.start();
创建一个AnimationTimer
并调用其start()
方法。
private class MyTimer extends AnimationTimer {
@Override
public void handle(long now) {
doHandle();
}
...
}
我们创建AnimationTimer
的具体子类并覆盖其handle()
方法。
private void doHandle() {
opacity -= 0.01;
lbl.opacityProperty().set(opacity);
if (opacity <= 0) {
stop();
System.out.println("Animation stopped");
}
}
在doHandle()
方法中,我们减小opacity
变量并更新opacityProperty
。 如果opacity
达到最小值,则使用stop()
方法停止计时器。
渐隐过渡
Transition
动画最适合计划的动画。 Transition
具有具体的类,可用于创建可以并行或顺序执行的各种动画; 例如FadeTransition
,PathTransition
,RotateTransition
或ScaleTransition
。
FadeTransition
创建一个跨越其持续时间的淡入淡出效果动画。 这是通过定期更新节点的opacity
变量来完成的。
FadeTransitionEx.java
package com.zetcode;
import javafx.animation.Animation;
import javafx.animation.FadeTransition;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
/**
* ZetCode JavaFX tutorial
*
* This program uses a FadeTransition. A rectangle
* fades out after we click into its area.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class FadeTransitionEx extends Application {
private FadeTransition ft;
private Rectangle rect;
@Override
public void start(Stage stage) {
initUI(stage);
}
private void initUI(Stage stage) {
Group root = new Group();
rect = new Rectangle(20, 20, 150, 150);
rect.setOnMouseClicked(new RectClickHandler());
ft = new FadeTransition(Duration.millis(5000), rect);
ft.setFromValue(1.0);
ft.setToValue(0.0);
root.getChildren().add(rect);
Scene scene = new Scene(root, 300, 250);
stage.setTitle("Fading transition");
stage.setScene(scene);
stage.show();
}
private class RectClickHandler implements EventHandler<MouseEvent> {
@Override
public void handle(MouseEvent event) {
doHandle();
}
private void doHandle() {
Double opa = rect.getOpacity();
if (opa.intValue() == 0) {
return;
}
Animation.Status as = ft.getStatus();
if (as == Animation.Status.RUNNING) {
return;
}
if (as == Animation.Status.STOPPED) {
ft.play();
}
}
}
public static void main(String[] args) {
Application.launch(args);
}
}
本示例使用FadeTransition
在矩形上创建淡出效果。 在矩形区域内单击鼠标后开始动画。
rect = new Rectangle(20, 20, 150, 150);
rect.setOnMouseClicked(new RectClickHandler());
鼠标单击的处理器设置为矩形。
ft = new FadeTransition(Duration.millis(5000), rect);
创建了FadeTransition
。 它的第一个参数是过渡的持续时间。 第二个参数是更新其opacity
参数的节点。
ft.setFromValue(1.0);
ft.setToValue(0.0);
setFromValue()
设置不透明度的开始值,setToValue()
设置不透明度的结束值。
Double opa = rect.getOpacity();
当前的不透明度值是通过getOpacity()
方法确定的。
if (opa.intValue() == 0) {
return;
}
矩形淡出后,我们取消了鼠标单击。
Animation.Status as = ft.getStatus();
if (as == Animation.Status.RUNNING) {
return;
}
if (as == Animation.Status.STOPPED) {
ft.play();
}
getStatus()
方法确定转换的状态。 如果状态为Animation.Status.STOPPED
,我们将使用play()
方法开始转换。
路径转换
PathTransition
沿路径创建动画。 通过更新节点的translateX
和translateY
变量来完成沿路径的转换。 请注意,我们必须使用支持元素绝对定位的节点。
PathTransitionEx.java
package com.zetcode;
import javafx.animation.PathTransition;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.CubicCurveTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.stage.Stage;
import javafx.util.Duration;
/**
* ZetCode JavaFX tutorial
*
* This program uses a PathTransition to move
* a circle along a path.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class PathTransitionEx extends Application {
@Override
public void start(Stage stage) {
initUI(stage);
}
private void initUI(Stage stage) {
Pane root = new Pane();
Path path = new Path();
path.getElements().add(new MoveTo(20, 120));
path.getElements().add(new CubicCurveTo(180, 60, 250, 340, 420, 240));
Circle circle = new Circle(20, 120, 10);
circle.setFill(Color.CADETBLUE);
PathTransition ptr = new PathTransition();
ptr.setDuration(Duration.seconds(6));
ptr.setDelay(Duration.seconds(2));
ptr.setPath(path);
ptr.setNode(circle);
ptr.setCycleCount(2);
ptr.setAutoReverse(true);
ptr.play();
root.getChildren().addAll(path, circle);
Scene scene = new Scene(root, 450, 300);
stage.setTitle("PathTransition");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
该示例使用PathTransition
沿路径移动圆。 动画在初始延迟 2 秒后开始。 它由两个周期组成。 动画受到尊敬; 也就是说,圆从起点到终点再返回。
Pane root = new Pane();
我们使用Pane
作为我们的根节点。 它支持动画所需的绝对定位。
Path path = new Path();
path.getElements().add(new MoveTo(20, 120));
path.getElements().add(new CubicCurveTo(180, 60, 250, 340, 420, 240));
在这里,我们定义了动画对象将沿其移动的Path
。
Circle circle = new Circle(20, 120, 10);
circle.setFill(Color.CADETBLUE);
这个圆是动画中的运动对象。
PathTransition ptr = new PathTransition();
创建一个PathTransition
对象。
ptr.setDuration(Duration.seconds(6));
setDuration()
方法设置动画的持续时间。
ptr.setDelay(Duration.seconds(2));
setDelay()
方法设置动画的初始延迟。
ptr.setPath(path);
ptr.setNode(circle);
setPath()
方法设置路径,setNode()
设置动画的目标节点。
ptr.setCycleCount(2);
我们的动画有两个循环。 循环次数通过setCycleCount()
方法设置。
ptr.setAutoReverse(true);
使用setAutoReverse()
方法,我们可以反转动画的方向。 圆会移回到起始位置。
ptr.play();
最后,play()
方法开始播放动画。
图:PathTransition
并行转换
ParallelTransition
并行播放Animations
的列表。
ParallelTransitionEx.java
package com.zetcode;
import javafx.animation.FillTransition;
import javafx.animation.ParallelTransition;
import javafx.animation.RotateTransition;
import javafx.animation.ScaleTransition;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
/**
* ZetCode JavaFX tutorial
*
* This program creates a parallel
* transition animation.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class ParallelTransitionEx extends Application {
@Override
public void start(Stage stage) {
initUI(stage);
}
private void initUI(Stage stage) {
Pane root = new Pane();
Rectangle rect = new Rectangle(50, 50, 30, 30);
rect.setArcHeight(10);
rect.setArcWidth(10);
rect.setFill(Color.CADETBLUE);
RotateTransition rottr
= new RotateTransition(Duration.millis(2000), rect);
rottr.setByAngle(180);
rottr.setCycleCount(2);
rottr.setAutoReverse(true);
ScaleTransition sctr = new ScaleTransition(Duration.millis(2000),
rect);
sctr.setByX(2);
sctr.setByY(2);
sctr.setCycleCount(2);
sctr.setAutoReverse(true);
FillTransition fltr = new FillTransition(Duration.millis(2000),
rect, Color.CADETBLUE, Color.STEELBLUE);
fltr.setCycleCount(2);
fltr.setAutoReverse(true);
root.getChildren().add(rect);
ParallelTransition ptr = new ParallelTransition();
ptr.getChildren().addAll(rottr, sctr, fltr);
ptr.play();
Scene scene = new Scene(root, 300, 250);
stage.setTitle("ParallelTransition");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
该示例并行播放三个过渡。 有一个旋转,缩放和背景颜色变化的矩形对象。
RotateTransition rottr
= new RotateTransition(Duration.millis(2000), rect);
rottr.setByAngle(180);
rottr.setCycleCount(2);
rottr.setAutoReverse(true);
RotateTransition
将矩形旋转指定角度。 旋转以两个周期发生并反转。
ScaleTransition sctr = new ScaleTransition(Duration.millis(2000),
rect);
sctr.setByX(2);
sctr.setByY(2);
ScaleTransition
将矩形放大和缩小 2 倍。
FillTransition fltr = new FillTransition(Duration.millis(2000),
rect, Color.CADETBLUE, Color.STEELBLUE);
FillTransition
将矩形的填充颜色从一种颜色值更改为另一种颜色值。
ParallelTransition ptr = new ParallelTransition();
ptr.getChildren().addAll(rottr, sctr, fltr);
ptr.play();
三种类型的过渡放置在ParallelTransition
中,它们并行(即同时)播放它们。
顺序转换
SequentialTransition
按顺序播放Animations
的列表。
SequentialTransitionEx.java
package com.zetcode;
import javafx.animation.FillTransition;
import javafx.animation.RotateTransition;
import javafx.animation.ScaleTransition;
import javafx.animation.SequentialTransition;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
/**
* ZetCode JavaFX tutorial
*
* This program plays three transitions in
* a sequential order.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class SequentialTransitionEx extends Application {
@Override
public void start(Stage stage) {
initUI(stage);
}
private void initUI(Stage stage) {
Pane root = new Pane();
Rectangle rect = new Rectangle(50, 50, 30, 30);
rect.setArcHeight(10);
rect.setArcWidth(10);
rect.setFill(Color.CADETBLUE);
RotateTransition rottr
= new RotateTransition(Duration.millis(2000), rect);
rottr.setByAngle(180);
rottr.setCycleCount(2);
rottr.setAutoReverse(true);
ScaleTransition sctr = new ScaleTransition(Duration.millis(2000),
rect);
sctr.setByX(2);
sctr.setByY(2);
sctr.setCycleCount(2);
sctr.setAutoReverse(true);
FillTransition fltr = new FillTransition(Duration.millis(2000),
rect, Color.CADETBLUE, Color.STEELBLUE);
fltr.setCycleCount(2);
fltr.setAutoReverse(true);
root.getChildren().add(rect);
SequentialTransition str = new SequentialTransition();
str.getChildren().addAll(rottr, sctr, fltr);
str.play();
Scene scene = new Scene(root, 300, 250);
stage.setTitle("SequentialTransition");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
该示例按顺序播放三个过渡-一个接一个。
SequentialTransition str = new SequentialTransition();
str.getChildren().addAll(rottr, sctr, fltr);
str.play();
这三个转换被添加到SequentialTransition
中。
时间线
Timeline
是使用 JavaFX 创建动画的最复杂的工具。 动画用KeyFrames
定义,其中包含更改的节点的属性。 这些属性封装在KeyValues
中。 Timeline
内插属性的更改。
TimelineEx.java
package com.zetcode;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.effect.Lighting;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
/**
* ZetCode JavaFX tutorial
*
* This program uses a Timeline to
* move a rectangle.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class TimelineEx extends Application {
@Override
public void start(Stage stage) {
initUI(stage);
}
private void initUI(Stage stage) {
Pane root = new Pane();
Rectangle rect = new Rectangle(20, 20, 60, 60);
rect.setEffect(new Lighting());
rect.setFill(Color.CADETBLUE);
Timeline tl = new Timeline();
tl.setCycleCount(2);
tl.setAutoReverse(true);
KeyValue kv = new KeyValue(rect.translateXProperty(), 200);
KeyFrame kf = new KeyFrame(Duration.millis(2000), kv);
tl.getKeyFrames().addAll(kf);
tl.play();
root.getChildren().addAll(rect);
Scene scene = new Scene(root, 350, 250);
stage.setTitle("Timeline");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
该示例使用Timeline
为矩形动画。
Rectangle rect = new Rectangle(20, 20, 60, 60);
rect.setEffect(new Lighting());
rect.setFill(Color.CADETBLUE);
此矩形是动画中的移动对象。
Timeline tl = new Timeline();
创建一个Timeline
对象。
tl.setCycleCount(2);
tl.setAutoReverse(true);
动画由两个循环组成,并且被反转。 矩形前后移动。
KeyValue kv = new KeyValue(rect.translateXProperty(), 200);
KeyValue
包含translateX
属性,该属性会随着时间变化为 200。
KeyFrame kf = new KeyFrame(Duration.millis(2000), kv);
实例化了KeyFrame
。 第一个参数是其持续时间,第二个参数是KeyValue
。 动画持续 2 秒钟,其translateX
属性更改为 200。
tl.getKeyFrames().addAll(kf);
关键帧将添加到帧列表。
顺序时间线动画
我们没有在时间轴中定义所有关键帧。 我们定义一些帧,其余的帧被插值。 关键帧在指定的时间点为时间轴内插的一组变量提供目标值。 为了顺序执行关键帧,我们利用了SequentialTransition
类。
SequentialTimelineEx.java
package com.zetcode;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.SequentialTransition;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;
/**
* ZetCode JavaFX tutorial
*
* This program creates a sequential Timeline
* animation.
*
* Author: Jan Bodnar
* Website: zetcode.com
* Last modified: June 2015
*/
public class SequentialTimelineEx extends Application {
@Override
public void start(Stage stage) {
initUI(stage);
}
private void initUI(Stage stage) {
Pane root = new Pane();
Circle c = new Circle(50, 100, 10);
c.setFill(Color.CADETBLUE);
KeyValue kv1 = new KeyValue(c.scaleXProperty(), 4);
KeyValue kv2 = new KeyValue(c.scaleYProperty(), 4);
KeyFrame kf1 = new KeyFrame(Duration.millis(3000), kv1, kv2);
Timeline scale = new Timeline();
scale.getKeyFrames().add(kf1);
KeyValue kv3 = new KeyValue(c.centerXProperty(), 250);
KeyFrame kf2 = new KeyFrame(Duration.millis(5000), kv3);
Timeline move = new Timeline();
move.getKeyFrames().add(kf2);
KeyValue kv4 = new KeyValue(c.scaleXProperty(), 1);
KeyValue kv5 = new KeyValue(c.scaleYProperty(), 1);
KeyFrame kf3 = new KeyFrame(Duration.millis(3000), kv4, kv5);
Timeline scale2 = new Timeline();
scale2.getKeyFrames().add(kf3);
SequentialTransition seqtr = new SequentialTransition(scale,
move, scale2);
seqtr.play();
root.getChildren().add(c);
Scene scene = new Scene(root, 300, 250);
stage.setTitle("Sequential Timeline animation");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
该示例创建顺序时间轴动画。 动画由三个Timelines
组成,它们与SequentialTransition
顺序执行。
Circle c = new Circle(50, 100, 10);
c.setFill(Color.CADETBLUE);
此动画中的移动对象是Circle
。
KeyValue kv1 = new KeyValue(c.scaleXProperty(), 4);
KeyValue kv2 = new KeyValue(c.scaleYProperty(), 4);
KeyFrame kf1 = new KeyFrame(Duration.millis(3000), kv1, kv2);
Timeline scale = new Timeline();
scale.getKeyFrames().add(kf1);
这是第一个Timeline
。 它会在三秒钟的时间内放大圆圈。
KeyValue kv3 = new KeyValue(c.centerXProperty(), 250);
KeyFrame kf2 = new KeyFrame(Duration.millis(5000), kv3);
Timeline move = new Timeline();
move.getKeyFrames().add(kf2);
第二个Timeline
将圆圈向前移动。 动画的这一部分持续五秒钟。
KeyValue kv4 = new KeyValue(c.scaleXProperty(), 1);
KeyValue kv5 = new KeyValue(c.scaleYProperty(), 1);
KeyFrame kf3 = new KeyFrame(Duration.millis(3000), kv4, kv5);
Timeline scale2 = new Timeline();
scale2.getKeyFrames().add(kf3);
第三个Timeline
按比例缩小圆圈。
SequentialTransition seqtr = new SequentialTransition(scale,
move, scale2);
seqtr.play();
这三个时间线放置在SequentialTransition
中。 时间轴依次播放。
在本章中,我们介绍了 JavaFX 动画。