JavaFX文档(7)开始JavaFX之旅——5 动画与视觉特效

时间:2023-01-19 17:04:56

声明:如需转载,请注明出处:http://blog.csdn.net/originer

原文地址:http://docs.oracle.com/javase/8/javafx/get-started-tutorial/animation.htm

你可以使用JavaFX来快速开发具有丰富用户体验的应用程序。本教程将会告诉你如何使用很少的代码来创建动画对象,并添加复杂的特效。

下面是程序运行后的效果截图:

JavaFX文档(7)开始JavaFX之旅——5 动画与视觉特效

下图展现了ColorfulCircles应用程序的场景图(scene graph)。枝干节点由Group类构成、叶子节点由Rectangle和Circle类构成。

JavaFX文档(7)开始JavaFX之旅——5 动画与视觉特效

创建应用程序

在IDE中创建名为名为ColorfulCircles程序。

创建Project

将ColorfulCircles类修改为如下代码:

public class ColorfulCircles extends Application {

@Override
public void start(Stage primaryStage) {
Group root = new Group();
Scene scene = new Scene(root, 800, 600, Color.BLACK);
primaryStage.setScene(scene);

primaryStage.show();
}

public static void main(String[] args) {
launch(args);
}
}
在ColorfulCircles应用程序中,使用了一个Group节点作为根节点。scene的节点大小直接进行了指定。然而在更多情况下可能需要使scene的大小与stage的大小而改变,如果需要如此,请自行参考之前的样例,使用layout来进行。


添加图形

下面添加30个圆圈:

Group circles = new Group();
for (int i = 0; i < 30; i++) {
Circle circle = new Circle(150, Color.web("white", 0.05));
circle.setStrokeType(StrokeType.OUTSIDE);
circle.setStroke(Color.web("white", 0.16));
circle.setStrokeWidth(4);
circles.getChildren().add(circle);
}
root.getChildren().add(circles);
这段代码创建了名为circles的group,然后 通过for循环创建了30个Circle并添加到circles中。每个圈的半径是150,填充背景为白色、opacity等级为5%,也就是说几乎是透明的。

代码中使用了StrokeType类来创建圆圈外的边。笔触类型(stroke type)为OUTSIDE意味着圆圈的边将会扩展到外部,并使用StrokeWidth作为宽度,在本例中是4。Stroke的颜色是白色、不透明等级为16%,使得它比圆圈的颜色要更为明亮一些。

最后一行代码将circles group添加到root node中。目前的结构是临时性的,后续将会按照之前设计的场景图进行修改。

下图展示了程序的运行效果。由于现在还没有为每个圆圈指定位置,因此它们都以窗口的左上角为中心重叠摆放。由于每个圆圈的不透明度设置,与黑色背景叠加后形成了灰色的效果。

JavaFX文档(7)开始JavaFX之旅——5 动画与视觉特效

添加视觉特效

下面为circles增加一个模糊效果,在primaryStage.show()之前插入代码:
circles.setEffect(new BoxBlur(10, 10, 3));
这行代码设置了一个模糊半径为10像素高、10像素宽、模糊迭代次数为3的模糊效果,与高斯模糊类似。这产生了如下图的模糊效果:

JavaFX文档(7)开始JavaFX之旅——5 动画与视觉特效

创建背景渐变

下面创建一个带有线性渐变的矩形,在root.getChildren().add(circles)之前插入下面的代码:
Rectangle colors = new Rectangle(scene.getWidth(), scene.getHeight(),
new LinearGradient(0f, 1f, 1f, 0f, true, CycleMethod.NO_CYCLE, new
Stop[]{
new Stop(0, Color.web("#f8bd55")),
new Stop(0.14, Color.web("#c0fe56")),
new Stop(0.28, Color.web("#5dfbc1")),
new Stop(0.43, Color.web("#64c2f8")),
new Stop(0.57, Color.web("#be4af7")),
new Stop(0.71, Color.web("#ed5fc2")),
new Stop(0.85, Color.web("#ef504c")),
new Stop(1, Color.web("#f2660f")),}));
colors.widthProperty().bind(scene.widthProperty());
colors.heightProperty().bind(scene.heightProperty());
root.getChildren().add(colors);
这段代码创建了一个名为colors的矩形,它与scen的宽和高相同,并且被一个由左下角(0,1)到右上角(1,0)的线性渐变所填充。其中的true表示渐变按矩形均匀地产生,NO_CYCLE表示颜色周期不会进行循环。Stop[]序列表示渐变的颜色点。
接下来的两行代码表示将scene和渐变矩形的大小绑定保持一致。后续的“JavaFX属性和绑定”教程将会更为详细地描述相关信息。
最后一行代码将colors矩形添加到根节点。
现在运行界面将会出现灰色的圆圈与彩虹一般的背景,如图:
JavaFX文档(7)开始JavaFX之旅——5 动画与视觉特效
下图表示了当前的场景图,此时circles group和colors矩形都是root节点的子节点。
JavaFX文档(7)开始JavaFX之旅——5 动画与视觉特效

添加混合模式

下面为圆圈增加颜色,并通过使用蒙版混合效果来使scene变暗。你需要从场景图中移除circles group和线性渐变矩形,然后将它们添加到新的蒙版混合group中。
1.定位如下两行代码:
root.getChildren().add(colors);
root.getChildren().add(circles);
2.用下面的代码替换上面的两行:
Group blendModeGroup = 
new Group(new Group(new Rectangle(scene.getWidth(), scene.getHeight(),
Color.BLACK), circles), colors);
colors.setBlendMode(BlendMode.OVERLAY);
root.getChildren().add(blendModeGroup);
blenModeGroup group创建了蒙版混合结构。在group中包含两个子节点。第一个是新建的Group,其中有一个黑色的矩形框,还有之前创建的circles group。第二个是之前创建的colors矩形。
setBlendMode()方法在colors矩形上增加了蒙版混合。最后一行代码将blendModeGroup添加到场景图的根节点上,现在场景图的结构就变得跟教程开头的图中一致了。
蒙版混合是图像处理应用程序中常见的一种效果。这种混合通过使用不同的混合颜色,可以将一个图片变暗或增加高光。在本例中线性渐变矩形被用作了蒙版。黑色矩形框用于保持背景是黑色,而近乎透明的圆圈则从渐变中获得了颜色并被变暗了。
下面是目前的运行效果,你将会在后面的教程中通过动画来看到最终效果:
JavaFX文档(7)开始JavaFX之旅——5 动画与视觉特效


添加动画

下面使用JavaFX动画来移动圆圈:
1:添加import static java.lang.Math.random;
2:在primaryStage.show()之前添加下面的代码来增加动画:
Timeline timeline = new Timeline();
for (Node circle: circles.getChildren()) {
timeline.getKeyFrames().addAll(
new KeyFrame(Duration.ZERO, // set start position at 0
new KeyValue(circle.translateXProperty(), random() * 800),
new KeyValue(circle.translateYProperty(), random() * 600)
),
new KeyFrame(new Duration(40000), // set end position at 40s
new KeyValue(circle.translateXProperty(), random() * 800),
new KeyValue(circle.translateYProperty(), random() * 600)
)
);
}
// play 40s of animation
timeline.play();

动画由一个时间线来驱动,因此这段代码创建了一个时间线,然后使用for循环为30个圆圈都增加了两个关键帧。第一个关键帧是在第0秒,通过使用 translateXProperty和translateYProperty属性来为圆圈设置一个随机位置。第二个关键帧是在第40秒,也是设置随机位置。因此当时间线开始play之后,它会将所有的圆圈在40秒之内从一个随机位置移到另外一个随机位置。
下面图展示了运行效果:
JavaFX文档(7)开始JavaFX之旅——5 动画与视觉特效
现在你的ColorfulCircles.java文件内容应该是这样的:
package colorfulcircles;import javafx.animation.KeyFrame;import javafx.animation.KeyValue;import javafx.animation.Timeline;import javafx.application.Application;import javafx.scene.Group;import javafx.scene.Node;import javafx.scene.Scene;import javafx.scene.effect.BlendMode;import javafx.scene.effect.BoxBlur;import javafx.scene.paint.Color;import javafx.scene.paint.CycleMethod;import javafx.scene.paint.LinearGradient;import javafx.scene.paint.Stop;import javafx.scene.shape.Circle;import javafx.scene.shape.Rectangle;import javafx.scene.shape.StrokeType;import javafx.stage.Stage;import javafx.util.Duration;import static java.lang.Math.random;public class ColorfulCircles extends Application {	@Override	public void start(Stage primaryStage) {		Group root = new Group();		Scene scene = new Scene(root, 800, 600, Color.BLACK);		primaryStage.setScene(scene);		Group circles = new Group();		for (int i = 0; i < 30; i++) {			Circle circle = new Circle(150, Color.web("white", 0.05));			circle.setStrokeType(StrokeType.OUTSIDE);			circle.setStroke(Color.web("white", 0.16));			circle.setStrokeWidth(4);			circles.getChildren().add(circle);		}		Rectangle colors = new Rectangle(scene.getWidth(), scene.getHeight(),				new LinearGradient(0f, 1f, 1f, 0f, true, CycleMethod.NO_CYCLE,						new Stop[] { new Stop(0, Color.web("#f8bd55")),								new Stop(0.14, Color.web("#c0fe56")),								new Stop(0.28, Color.web("#5dfbc1")),								new Stop(0.43, Color.web("#64c2f8")),								new Stop(0.57, Color.web("#be4af7")),								new Stop(0.71, Color.web("#ed5fc2")),								new Stop(0.85, Color.web("#ef504c")),								new Stop(1, Color.web("#f2660f")), }));		colors.widthProperty().bind(scene.widthProperty());		colors.heightProperty().bind(scene.heightProperty());		// root.getChildren().add(colors);		// root.getChildren().add(circles);		Group blendModeGroup = new Group(new Group(new Rectangle(				scene.getWidth(), scene.getHeight(), Color.BLACK), circles),				colors);		colors.setBlendMode(BlendMode.OVERLAY);		root.getChildren().add(blendModeGroup);		circles.setEffect(new BoxBlur(20, 20, 3));		Timeline timeline = new Timeline();		for (Node circle : circles.getChildren()) {			timeline.getKeyFrames().addAll(					new KeyFrame(Duration.ZERO, // set start position at 0							new KeyValue(circle.translateXProperty(),									random() * 800),							new KeyValue(circle.translateYProperty(),									random() * 600)),					new KeyFrame(new Duration(40000), // set end position at 40s							new KeyValue(circle.translateXProperty(),									random() * 800), new KeyValue(circle									.translateYProperty(), random() * 600)));		}		// play 40s of animation		timeline.play();		primaryStage.show();	}	public static void main(String[] args) {		launch(args);	}}