当前位置: 首页 > news >正文

Java 实现桌面烟花秀

前言

今天,我们将展示如何使用 Java Swing 创建一个烟花效果,覆盖整个桌面。我们将重点讲解如何在桌面上展示烟花、如何实现发射和爆炸效果,以及如何将这些效果整合到一个完整的程序中。

效果展示

如上图所示,我们在桌面实现了:

  1. 发射烟花
  2. 烟花爆炸的特效

完整代码

下面是整个项目的完整代码。大家可以新建一个名为 FireworkDisplay.java 的文件,将完整代码拷贝进去,运行 main() 方法,就可以在桌面看到一场烟花秀了。

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Random;// 发射粒子类
class LaunchParticle {public int x, y;       // 位置public int vx, vy;     // 速度public boolean exploded = false; // 是否已经爆炸public Color color;    // 粒子颜色public int targetHeight;  // 目标高度public LaunchParticle(int startX, int startY, int velocityX, int velocityY, int targetHeight, Color color) {this.x = startX;this.y = startY;this.vx = velocityX;this.vy = velocityY;this.targetHeight = targetHeight;this.color = color;}// 更新发射粒子的位置public void update() {x += vx;y += vy;vy += 1;  // 模拟重力// 当粒子到达目标高度或者开始下落时爆炸if (y <= targetHeight || vy >= 0) {exploded = true;}}
}// 烟花爆炸粒子类
class FireworkParticle {public int x, y;       // 位置public int vx, vy;     // 速度public int life;       // 粒子寿命public Color color;    // 粒子颜色public FireworkParticle(int x, int y, int vx, int vy, int life, Color color) {this.x = x;this.y = y;this.vx = vx;this.vy = vy;this.life = life;this.color = color;}// 更新爆炸粒子的位置public void update() {x += vx;y += vy;vy += 1;  // 模拟重力life--;}public boolean isDead() {return life <= 0;}
}// 显示烟花的面板
class FireworkPanel extends JPanel implements ActionListener {private final ArrayList<FireworkParticle> particles = new ArrayList<>();private final ArrayList<LaunchParticle> launchParticles = new ArrayList<>();private final Random random = new Random();private Timer timer;private int screenHeight;// 预定义一组颜色段,确保在任何背景下都好看private final Color[] colorPalette = {new Color(255, 128, 0),   // 橙色new Color(255, 51, 51),   // 红色new Color(51, 204, 255),  // 天蓝色new Color(51, 255, 51),   // 绿色new Color(204, 51, 255),  // 紫色new Color(255, 255, 51),  // 黄色new Color(255, 102, 178)  // 粉色};public FireworkPanel(int screenHeight) {this.screenHeight = screenHeight;// 设置面板为透明this.setOpaque(false);timer = new Timer(30, this);  // 每 30 毫秒更新一次timer.start();}// 选择随机颜色,从预定义的颜色段中选取private Color getRandomColor() {return colorPalette[random.nextInt(colorPalette.length)];}// 添加发射粒子public void addLaunchParticle() {int width = getWidth();int height = getHeight();if (width <= 0 || height <= 0) return;int startX = random.nextInt(width); // 发射起始位置int startY = height; // 从屏幕底部发射int targetHeight = (int) (screenHeight * 0.2); // 烟花将在屏幕高度的 4/5 处爆炸int velocityY = -(random.nextInt(10) + 50); // 增加初始向上速度,确保高度合适Color color = getRandomColor(); // 从预定义颜色中选取launchParticles.add(new LaunchParticle(startX, startY, 0, velocityY, targetHeight, color));}// 添加爆炸粒子,使用极坐标生成圆形分布,增加中心粒子密度public void addExplosion(int x, int y, Color color) {int numParticles = 200; // 每个烟花的粒子数量for (int i = 0; i < numParticles; i++) {double angle = 2 * Math.PI * random.nextDouble(); // 每个粒子的角度随机int speed = random.nextInt(15) + 5; // 粒子的速度增加随机性,靠近中心的速度会更小int vx = (int) (Math.cos(angle) * speed); // 使用极坐标计算x方向速度int vy = (int) (Math.sin(angle) * speed); // 使用极坐标计算y方向速度int life = random.nextInt(50) + 50; // 粒子寿命particles.add(new FireworkParticle(x, y, vx, vy, life, color));}}@Overrideprotected void paintComponent(Graphics g) {super.paintComponent(g);Graphics2D g2d = (Graphics2D) g;// 绘制发射粒子for (LaunchParticle launchParticle : launchParticles) {g2d.setColor(launchParticle.color);g2d.fillOval(launchParticle.x - 3, launchParticle.y - 3, 6, 10); // 缩小发射粒子尺寸,使用半径为3的圆形}// 绘制爆炸粒子for (FireworkParticle particle : particles) {g2d.setColor(new Color(particle.color.getRed(), particle.color.getGreen(), particle.color.getBlue(), Math.max(particle.life * 255 / 100, 0)));g2d.fillOval(particle.x - 3, particle.y - 3, 6, 6); // 缩小爆炸粒子尺寸,使用半径为3的圆形}}@Overridepublic void actionPerformed(ActionEvent e) {// 更新发射粒子ArrayList<LaunchParticle> toRemoveLaunch = new ArrayList<>();for (LaunchParticle launchParticle : launchParticles) {launchParticle.update();if (launchParticle.exploded) {addExplosion(launchParticle.x, launchParticle.y, launchParticle.color); // 发射粒子爆炸toRemoveLaunch.add(launchParticle);}}launchParticles.removeAll(toRemoveLaunch);// 更新爆炸粒子ArrayList<FireworkParticle> deadParticles = new ArrayList<>();for (FireworkParticle particle : particles) {particle.update();if (particle.isDead()) {deadParticles.add(particle);}}particles.removeAll(deadParticles);// 持续生成发射粒子,保证烟花不断生成if (random.nextInt(15) == 0) { // 增加生成频率addLaunchParticle(); // 随机生成多个烟花}// 重新绘制repaint();}
}public class FireworkDisplay {public static void main(String[] args) {// 获取屏幕大小Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();// 创建全屏的透明窗口JWindow window = new JWindow();window.setSize(screenSize);window.setBackground(new Color(0, 0, 0, 0)); // 设置背景透明window.setLayout(new BorderLayout()); // 使用 BorderLayout 布局// 添加烟花面板FireworkPanel panel = new FireworkPanel(screenSize.height); // 传递屏幕高度window.add(panel, BorderLayout.CENTER); // 确保 FireworkPanel 占满整个窗口window.setAlwaysOnTop(true); // 窗口始终在最前面window.setVisible(true);// 强制重绘刷新RepaintManager.currentManager(panel).markCompletelyDirty(panel);// 启动后立即生成发射粒子panel.addLaunchParticle();}
}

全屏透明窗口

为了让烟花在桌面上显示而不是局限在普通的应用程序窗口中,我们需要创建一个 全屏透明窗口。我们使用 JWindow 类,这是一个没有边框和标题栏的窗口,非常适合用于全屏特效展示。

为什么需要全屏透明窗口?

普通的窗口只会在应用程序区域内显示,而我们希望烟花能够在整个桌面上绽放。通过设置一个透明的 JWindow,我们就能让窗口完全覆盖桌面。

Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); // 获取屏幕大小
JWindow window = new JWindow(); // 创建全屏窗口
window.setSize(screenSize); // 设置窗口为屏幕大小
window.setBackground(new Color(0, 0, 0, 0)); // 使窗口背景完全透明
window.setLayout(new BorderLayout());

window.setBackground(new Color(0, 0, 0, 0)); 中的最后一个参数 0 表示完全透明。这样一来,窗口显示的部分就是我们后续的烟花效果,桌面背景不会被覆盖。

粒子系统

我们通过粒子的方式来模拟烟花的发射效果。首先,发射粒子从屏幕底部向上移动。我们创建 LaunchParticle 类来管理这些粒子。每个粒子都有位置、速度等属性,它们随着时间不断更新位置,模拟上升的过程。

class LaunchParticle {public int x, y; // 粒子的位置public int vx, vy; // 粒子的速度public boolean exploded = false; // 是否已爆炸public LaunchParticle(int startX, int startY, int velocityX, int velocityY) {this.x = startX;this.y = startY;this.vx = velocityX;this.vy = velocityY;}public void update() {this.x += this.vx; // 更新水平位置this.y += this.vy; // 更新垂直位置}
}

每个粒子都有初始的位置和速度,我们通过 update() 方法来不断改变它们的位置,形成粒子上升的效果。

当发射粒子上升到一定高度时,粒子就会爆炸。爆炸后,它们会分裂成多个小粒子,这些粒子四处扩散,就可以模拟出烟花绽放的效果。我们通过 FireworkParticle 类来实现这些爆炸后的粒子。

class FireworkParticle {public int x, y;public int vx, vy; // 爆炸粒子的速度public int life; // 粒子的生命值public FireworkParticle(int x, int y, int vx, int vy, int life) {this.x = x;this.y = y;this.vx = vx;this.vy = vy;this.life = life;}public void update() {this.x += this.vx; // 更新位置this.y += this.vy;this.life--; // 生命值减少}public boolean isDead() {return life <= 0; // 粒子是否存活}
}

在爆炸发生时,我们会生成多个 FireworkParticle,每个粒子的速度和方向都是随机的,这样可以让烟花的爆炸效果更加多样化。

结语

本期的分享就结束了,有兴趣的小伙伴可以思考一下如何实现更多有趣的特效哦。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 树——数据结构
  • 计算机网络第二章:作业 1: Web 服务器
  • 相机光学(三十七)——自动对焦原理
  • 2018年国赛高教杯数学建模D题汽车总装线的配置问题解题全过程文档及程序
  • OpenCV高阶操作
  • 耐压110V茂睿芯MK9019可以向下兼容MK9016
  • 找了一家征信公司,干了两个多月。。。
  • 【每日一诗】【诗词创作】【诗】《雨前秋夜》
  • mysql 修改索引
  • Spring Boot- 配置中心问题
  • 深度学习速通系列:TextCNN介绍
  • Ubuntu20.04点击文件闪退
  • 海康威视摄像机和录像机的监控与回放
  • 计算机毕业设计 家电销售展示平台的设计与实现 Java实战项目 附源码+文档+视频讲解
  • 代码随想录算法训练营第13天|二叉树基础知识、递归遍历、迭代遍历、层序遍历、116. 填充每个节点的下一个右侧节点指针
  • 【干货分享】SpringCloud微服务架构分布式组件如何共享session对象
  • cookie和session
  • JavaScript DOM 10 - 滚动
  • Java多态
  • Laravel核心解读--Facades
  • Spring Cloud(3) - 服务治理: Spring Cloud Eureka
  • 案例分享〡三拾众筹持续交付开发流程支撑创新业务
  • 第13期 DApp 榜单 :来,吃我这波安利
  • 如何学习JavaEE,项目又该如何做?
  • 设计模式走一遍---观察者模式
  • 使用common-codec进行md5加密
  • 微信端页面使用-webkit-box和绝对定位时,元素上移的问题
  • 移动互联网+智能运营体系搭建=你家有金矿啊!
  • 用Canvas画一棵二叉树
  • 整理一些计算机基础知识!
  • # Pytorch 中可以直接调用的Loss Functions总结:
  • ( 用例图)定义了系统的功能需求,它是从系统的外部看系统功能,并不描述系统内部对功能的具体实现
  • (ISPRS,2023)深度语义-视觉对齐用于zero-shot遥感图像场景分类
  • (void) (_x == _y)的作用
  • (vue)el-checkbox 实现展示区分 label 和 value(展示值与选中获取值需不同)
  • (阿里巴巴 dubbo,有数据库,可执行 )dubbo zookeeper spring demo
  • (笔试题)合法字符串
  • (层次遍历)104. 二叉树的最大深度
  • (附源码)springboot课程在线考试系统 毕业设计 655127
  • (附源码)ssm基于jsp高校选课系统 毕业设计 291627
  • (亲测有效)推荐2024最新的免费漫画软件app,无广告,聚合全网资源!
  • (学习日记)2024.03.12:UCOSIII第十四节:时基列表
  • ./和../以及/和~之间的区别
  • .NET Framework 和 .NET Core 在默认情况下垃圾回收(GC)机制的不同(局部变量部分)
  • .NET 将混合了多个不同平台(Windows Mac Linux)的文件 目录的路径格式化成同一个平台下的路径
  • .netcore 如何获取系统中所有session_如何把百度推广中获取的线索(基木鱼,电话,百度商桥等)同步到企业微信或者企业CRM等企业营销系统中...
  • .NET单元测试使用AutoFixture按需填充的方法总结
  • .project文件
  • @property括号内属性讲解
  • [ Socket学习 ] 第一章:网络基础知识
  • [ vulhub漏洞复现篇 ] Apache Flink目录遍历(CVE-2020-17519)
  • [120_移动开发Android]008_android开发之Pull操作xml文件
  • [1204 寻找子串位置] 解题报告
  • [AIGC] 如何建立和优化你的工作流?
  • [C#]实现GRPC通讯的服务端和客户端实例