JavaFX 动画简介
此页面由 Connor Schweighöfer 在 UPL 下贡献The javafx.animation 包提供了一个简单的框架,用于在 JavaFX 应用程序中创建动画和过渡。它基于 WritableValue<T>
的原理,该原理在 JavaFX 中被广泛使用。WritableValue<T>
是一个接口,它封装了一个可以读取和设置的值。它通常用于存储 JavaFX UI 元素中的属性,例如 Rectangle
形状中的 width
或 height
。它还提供各种内置过渡以实现常见效果,支持并行和顺序过渡,以及在动画完成时处理事件的能力。
本文介绍了所有类型的动画,从 Animation
及其子类 Transition
和 Timeline
开始,然后使用 AnimationTimer
表示更低级的动画。虽然 Transition
提供了一种更简单、更用户友好的创建动画的方式,但 Timeline
提供了更大的灵活性,适用于更复杂的动画。相比之下,AnimationTimer
专为逐帧更新而设计,不使用 WritableValue<T>
。
动画
抽象类 Animation
提供了 Transition
和 Timeline
动画的核心功能,不能直接扩展。
一个 Animation
包含多个属性
targetFramerate
是此Animation
运行的最大帧速率(每秒帧数)。currentTime
是Animation
中的当前时间点,以Duration
表示。rate
定义了Animation
预期播放的方向和速度。它支持正数和负数。cycleCount
定义了此Animation
的循环次数。它在运行时不能更改,并且必须为正数。cycleDuration
是此Animation
一个循环的Duration
。它是Animation
从开始到结束播放所需的时间,以默认速率 1.0 播放。totalDuration
指示此Animation
的总持续时间,包括重复。它是cycleDuration * cycleCount
或Duration.INDEFINITE
的结果。delay
是Animation
开始时的延迟Duration
。autoReverse
属性指定Animation
是否将在交替循环中以反向播放。onFinished
事件处理程序用于定义Animation
完成时的附加行为。status
表示Animation
的当前状态,可能的状态包括PAUSED
、RUNNING
和STOPPED
。
此外,它还提供了一些有用的方法,例如 play()
、playFrom(String cuePoint)
、pause()
、stop()
等,用于控制动画流程。快速浏览 其文档 可以很好地概述其功能。
过渡
The Transition
抽象类是所有过渡的基础类,它提供了一种常见的 Animation
形式。JavaFX 为常见的 Node
和 Shape
属性提供了各种内置过渡。
淡入淡出过渡
The FadeTransition
创建淡入淡出效果。这是通过定期更新 Node
的 opacity
属性来实现的。
Circle circle = new Circle(150, 150, 20, Color.GREEN);
FadeTransition transition = new FadeTransition(Duration.seconds(5), circle);
transition.setFromValue(1.0);
transition.setToValue(0);
transition.setInterpolator(Interpolator.LINEAR);
transition.play();
(有关设置 JavaFX 应用程序的完整指南,请参阅本文:JavaFX 应用程序基本结构示例)
填充过渡
The FillTransition
创建一个动画,该动画会更改形状的填充。这是通过定期更新 Shape
的 fill
属性来实现的。
Circle circle = new Circle(150, 150, 20, Color.GREEN);
FillTransition transition = new FillTransition(Duration.seconds(5), circle);
transition.setFromValue(Color.GREEN);
transition.setToValue(Color.BLACK);
transition.setInterpolator(Interpolator.LINEAR);
transition.play();
平移过渡
The TranslateTransition
创建一个从一个位置到另一个位置的直线移动/平移动画。这是通过定期更新 Node
的 translateX
、translateY
和 translateZ
属性来实现的。
Circle circle = new Circle(50, 50, 10, Color.GREEN);
TranslateTransition transition = new TranslateTransition(Duration.seconds(5), circle);
transition.setToX(200);
transition.setToY(200);
transition.setInterpolator(Interpolator.LINEAR);
transition.play();
路径过渡
The PathTransition
创建一个使用由一系列形状指定的复杂预定义路径的移动动画。沿着路径的平移是通过定期更新 Node
的 translateX
和 translateY
属性来实现的,如果 orientation
设置为 OrientationType.ORTHOGONAL_TO_TANGENT
,则 rotate
变量也会得到更新。
Circle circle = new Circle(50, 50, 10, Color.GREEN);
Path path = new Path();
path.getElements().add(new MoveTo(50, 50)); // starting point
path.getElements().add(new LineTo(250, 250));
PathTransition transition = new PathTransition(Duration.seconds(5), path, circle);
transition.setInterpolator(Interpolator.LINEAR);
transition.play();
旋转过渡
The RotateTransition
创建一个旋转动画。这是通过定期更新 Node
的 rotate
属性来实现的。角度值以度为单位指定。
Rectangle rectangle = new Rectangle(125, 125, 50, 50);
rectangle.setFill(Color.GREEN);
RotateTransition transition = new RotateTransition(Duration.seconds(5), rectangle);
transition.setFromAngle(0);
transition.setToAngle(360);
transition.setInterpolator(Interpolator.LINEAR);
transition.play();
缩放过渡
The ScaleTransition
创建一个缩放动画,该动画会更改节点的大小。这是通过定期更新 Node
的 scaleX
、scaleY
和 scaleZ
属性来实现的。
Circle circle = new Circle(150, 150, 50, Color.GREEN);
ScaleTransition transition = new ScaleTransition(Duration.seconds(5), circle);
transition.setToX(0.1);
transition.setToY(0.1);
transition.setInterpolator(Interpolator.LINEAR);
transition.play();
描边过渡
The StrokeTransition
创建一个动画,该动画会更改形状的描边颜色。这是通过定期更新 Shape
的 stroke
属性来实现的。
Circle circle = new Circle(150, 150, 50, Color.GREEN);
circle.setStrokeWidth(5);
StrokeTransition transition = new StrokeTransition(Duration.seconds(5), circle);
transition.setFromValue(Color.GREEN);
transition.setToValue(Color.BLACK);
transition.setInterpolator(Interpolator.LINEAR);
transition.play();
顺序过渡
The SequentialTransition
按顺序播放一系列动画。不建议包含一个 Animation
,该 Animation
不是最后一个具有 Duration.INDEFINITE
的 Animation
,因为这将阻止序列中的所有后续动画。
暂停过渡
The PauseTransition
为指定的 duration
创建一个暂停。此行为对于在 SequentialTransition
中创建延迟很有用,在 SequentialTransition
中没有属性更改。
Circle circle = new Circle(150, 150, 20, Color.GREEN);
circle.setStrokeWidth(5);
ScaleTransition smaller = new ScaleTransition(Duration.seconds(1.5));
smaller.setToX(0.25);
smaller.setToY(0.25);
smaller.setInterpolator(Interpolator.LINEAR);
ScaleTransition larger = new ScaleTransition(Duration.seconds(1.5));
larger.setToX(1);
larger.setToY(1);
larger.setInterpolator(Interpolator.LINEAR);
SequentialTransition transition = new SequentialTransition(
circle,
smaller,
new PauseTransition(Duration.seconds(2)),
larger
);
transition.play();
请注意,此代码仅在 SequentialTransition
上设置一个 Node
,它是此处的父过渡,而不是在各个子过渡上设置。它们将隐式使用其父过渡的 Node
。
并行过渡
The ParallelTransition
并行播放一组动画。
Rectangle rectangle = new Rectangle(50, 50, 10, 10);
rectangle.setFill(Color.GREEN);
TranslateTransition translate = new TranslateTransition(Duration.seconds(5));
translate.setToX(200);
translate.setToY(200);
translate.setInterpolator(Interpolator.LINEAR);
RotateTransition rotate = new RotateTransition(Duration.seconds(5));
rotate.setFromAngle(0);
rotate.setToAngle(360);
rotate.setInterpolator(Interpolator.LINEAR);
ParallelTransition transition = new ParallelTransition(rectangle, translate, rotate);
transition.play();
时间轴
A Timeline
用于在任何 WritableValue<T>
上定义自由形式的 Animation
。如果没有任何内置过渡在所需的属性上运行,它将很有用。它由一系列顺序的 KeyFrame
组成,每个 KeyFrame
都封装了时间中的一个时刻。它们共同指定了目标属性在整个持续时间内的演变方式。
警告:正在从 FX 运行时引用正在运行的
Timeline
。在无限时间轴中,具有动画属性的对象将不会被垃圾回收,这可能会导致内存泄漏。因此,请确保在不再需要时间轴实例时停止它。
关键帧
A KeyFrame
表示动画序列中的特定时刻(提示点),并包含一组 KeyValue
实例,这些实例在给定的 Duration
内发生变化。一个 KeyFrame 可以有一个名称,然后可以使用该名称在动画中识别此 KeyFrame
,例如,使用 playFrom(String cuePoint)
从此特定 KeyFrame
开始。还可以提供 onFinished
实现,该实现将在命中此提示点时被调用。
关键值
A KeyValue
在 WritableValue<T>
和类型为 T
的目标值之间建立映射。这用于定义值的更改。还可以额外定义一个 Interpolator
来设置此值的更改速率。KeyValue
类是不可变的。
示例
此 Timeline
示例创建一个 Circle
,它在 5 秒的持续时间内在 x 方向上移动 200 像素
Circle circle = new Circle(50, 150, 10, Color.GREEN);
KeyValue x = new KeyValue(circle.translateXProperty(), 200);
KeyFrame frame = new KeyFrame(Duration.seconds(5), x);
Timeline timeline = new Timeline(frame);
timeline.play();
插值器
The Interpolator
抽象类定义了值随时间变化的速率,影响动画的平滑度。有几种内置实现用于常见的插值技术。
注意:默认情况下,所有过渡(ParallelTransition
和 SequentialTransition
除外)都使用 Interpolator#EASE_BOTH
。
以下是使用 Timeline
中的示例对 Interpolator
进行可视化的示例
离散
The Interpolator.DISCRETE
插值器在值之间创建突然的过渡,没有任何中间步骤。
Circle circle = new Circle(50, 150, 10, Color.GREEN);
KeyValue x = new KeyValue(circle.translateXProperty(), 200, Interpolator.DISCRETE);
KeyFrame frame = new KeyFrame(Duration.seconds(5), x);
Timeline timeline = new Timeline(frame);
timeline.play();
线性
The Interpolator.LINEAR
插值器在时间内产生值之间的恒定变化速率。
缓入
Interpolator.EASE_IN
插值器 从缓慢开始,随着动画的进行逐渐加速。
缓出
Interpolator.EASE_OUT
插值器 从快速开始,随着动画的进行逐渐减速。
缓进缓出
Interpolator.EASE_BOTH
插值器 从缓慢开始,在中间加速,然后在结束时减速。它结合了 EASE_IN
和 EASE_OUT
的特性。
此外,还有两个用于 Interpolator.SPLINE
和 Interpolator.TANGENT
插值的静态工厂方法。
动画计时器
AnimationTimer
抽象类 提供了创建动画的最低级别选项。handle(long now)
方法在动画处于活动状态时在每一帧中被调用。时间戳 now
是当前帧的时间(以纳秒为单位),对于在该帧中调用的所有 AnimationTimer
来说都是相同的。此外,AnimationTimer
还添加了 start()
和 stop()
方法来处理动画的生命周期。
注意:handle
方法将在 JavaFX 应用程序线程 中调用,因此应避免长时间运行和阻塞操作。为了保持每秒 30 帧的流畅帧速率,整个 JavaFX 应用程序理想情况下每帧分配不超过 33 毫秒。
结论
在本教程中,您已经探索了 javafx.animation
包,并学习了如何在 JavaFX 应用程序中创建动态动画。我们首先了解了基础的 Animation
类,然后继续学习 Transition
和 Timeline
类,它们提供了创建和控制动画的不同方法。此外,您还学习了如何通过几个 Interpolator
示例来控制动画的进度。最后,我们介绍了 AnimationTimer
类,它允许进行精确的逐帧更新动画。有了这些工具,您现在就可以在 JavaFX 应用程序中创建丰富的动画了。
上次更新: 2024 年 5 月 31 日
返回教程列表