直接上代码
package sample;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
// 主类,继承自Application,是JavaFX应用程序的入口
public class Main extends Application {
// 定义常量,表示火花的数量为100个
private static final int SPARK_COUNT = 100;
// 用于存储所有火花对象的列表
private List<Spark> sparks = new ArrayList<>();
// 用于绘图的画布对象
private Canvas canvas;
// 绕X轴的旋转角度,初始化为0
private double rotationAngleX = 0;
// 绕Y轴的旋转角度,初始化为0
private double rotationAngleY = 0;
// 旋转速度,用于控制角度的变化速率
private double rotationSpeed = 0.01;
//在屏幕的什么范围内生成
private Integer xZone=1920;
private Integer yZone=1000;
// JavaFX应用程序启动时调用的方法
@Override
public void start(Stage primaryStage) throws Exception {
// 创建一个根节点Group,用于组织场景中的所有节点
Group root = new Group();
// 创建一个宽度为800,高度为600的画布
canvas = new Canvas(xZone, yZone);
// 将画布添加到根节点中
root.getChildren().add(canvas);
// 循环创建100个火花对象,并添加到sparks列表中
for (int i = 0; i < SPARK_COUNT; i++) {
sparks.add(new Spark(xZone,yZone));
}
// 创建一个动画定时器,用于定时更新和绘制画面
AnimationTimer timer = new AnimationTimer() {
// 每次定时器触发时调用的方法
@Override
public void handle(long now) {
// 获取画布的绘图上下文,用于绘制图形
GraphicsContext gc = canvas.getGraphicsContext2D();
// 清空画布上的内容
gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
// 保存当前的绘图状态,以便后续恢复
gc.save();
// 将绘图原点平移到画布的中心
gc.translate(canvas.getWidth() / 2, canvas.getHeight() / 2);
// 增加绕X轴和Y轴的旋转角度
rotationAngleX += rotationSpeed;
rotationAngleY += rotationSpeed;
// 遍历所有的火花对象
for (Spark spark : sparks) {
// 更新火花的状态(例如透明度)
spark.update();
// 计算火花在3D空间中的初始坐标(相对于画布中心的偏移)
double x3d = spark.x - canvas.getWidth() / 2;
double y3d = spark.y - canvas.getHeight() / 2;
double z = spark.depth;
// 进行绕X轴的旋转计算,更新Y和Z坐标
double newY = y3d * Math.cos(rotationAngleX) - z * Math.sin(rotationAngleX);
z = y3d * Math.sin(rotationAngleX) + z * Math.cos(rotationAngleX);
y3d = newY;
// 进行绕Y轴的旋转计算,更新X和Z坐标
double newX = x3d * Math.cos(rotationAngleY) + z * Math.sin(rotationAngleY);
z = -x3d * Math.sin(rotationAngleY) + z * Math.cos(rotationAngleY);
x3d = newX;
// 计算透视变换的因子,用于模拟3D效果
double factor = 1000.0 / (1000.0 + z);
// 计算在屏幕上的X坐标
double screenX = x3d * factor + canvas.getWidth() / 2;
// 计算在屏幕上的Y坐标
double screenY = y3d * factor + canvas.getHeight() / 2;
// 计算在屏幕上显示的火花大小
double size = spark.size * factor;
// 设置绘图颜色,根据火花的颜色和透明度进行调整
gc.setFill(spark.color.deriveColor(0, 1, 1, spark.alpha));
// 在计算后的坐标位置绘制火花(一个椭圆)
gc.fillOval(screenX - size / 2, screenY - size / 2, size, size);
}
// 移除透明度为0(即已经消失)的火花
sparks.removeIf(spark -> spark.alpha == 0);
// 如果火花数量不足100个,添加新的火花
while (sparks.size() < SPARK_COUNT) {
sparks.add(new Spark(xZone,yZone));
}
// 恢复之前保存的绘图状态
gc.restore();
}
};
// 启动动画定时器
timer.start();
// 设置窗口的样式为透明
primaryStage.initStyle(StageStyle.TRANSPARENT);
// 创建一个场景,包含根节点,宽度为800,高度为600,背景透明
Scene scene = new Scene(root, xZone, yZone, Color.TRANSPARENT);
// 将场景设置到主舞台上
primaryStage.setScene(scene);
// 显示主舞台
primaryStage.show();
}
// 应用程序的入口点,启动JavaFX应用程序
public static void main(String[] args) {
launch(args);
}
}
// 火花类,用于表示一个火花对象
class Spark {
// 火花在画布上的X坐标
double x;
// 火花在画布上的Y坐标
double y;
// 火花的大小
double size;
// 火花的颜色
Color color;
// 火花的透明度
double alpha;
// 火花在3D空间中的深度值
double depth;
// 构造方法,用于初始化火花的属性
public Spark(int xZone,int yZone) {
Random random = new Random();
// 随机生成X坐标,范围在0到800之间
x = random.nextInt(xZone);
// 随机生成Y坐标,范围在0到600之间
y = random.nextInt(yZone);
// 随机生成大小,范围在2到102之间
size = random.nextDouble() * 100 + 10;
// 随机生成颜色
color = Color.color(random.nextDouble(), random.nextDouble(), random.nextDouble());
// 初始透明度为1.0(完全不透明)
alpha = 1.0;
// 随机生成深度值,范围在0到1000之间
depth = random.nextDouble() * 1000;
}
// 更新火花状态的方法,主要是减少透明度
public void update() {
alpha -= 0.01;
if (alpha < 0) {
alpha = 0;
}
}
}