【HTML】过年不能放烟花,那就放电子烟花

闲谈

大家回家过年可能都多多少少放过些🧨,但是有些在城市上过年的小伙伴可能就没有机会放鞭炮了。不过没关系,我们懂技术,我们用技术自娱自乐,放电子烟花,总不可能被警长叔叔敲门问候吧。

开干

首先,我们先明确一下思路,我觉得可以分解为如下2个步骤。涉及到Canvas+requestAnimationFrame+物理知识,🐶。

  1. 我们先画出一个烟花爆炸出来的粒子,这涉及到技术Canvas + 物理知识
  2. 最后通过动画将多个粒子的运动轨迹连在一起即可。

大家看下,我感觉应该没什么问题了,于是深入细节分析。

初始化粒子

我们先分析一下这个粒子有哪些属性,我罗列如下

  • 粒子的初始坐标(x,y)
  • 粒子的初始速度(Vx,Vy)
  • 粒子的颜色(Color)
  • 粒子的半径(Radius)
  • 粒子的透明度,随着粒子的落下,粒子的亮度会逐渐减小(opacity)

属性分析完了,接下来我们思考一个问题,粒子在爆炸那一刹那,是如何运动的呢?
很显然,是带着初速度的自由落体运动。
image.png
我们将速度分成水平方向Vx以及竖直方向Vy,于是我们可以得到

  • Vx在运动中是不变的
  • Vy的速度为Vy = Vy - gt,也就是每秒会速度下降g,由于g是个定值,在模拟的时候我们就取每帧(屏幕刷新率,大概10ms一次)下降0.15px速度。

注意: Vy的速度在代码里是Vy += 0.15,为什么是加呢,因为对于屏幕而言,右上角的px是(0,0),所以想下的速度是正,向上的速度是负。

于是我们便可撰写如下代码

class Dot {constructor(x, y, color, Vx, Vy) {this.x = x;this.y = y;this.Vx = Vx;this.Vy = Vy;this.color = color;this.radius = 2.5;this.opacity = 1;}update() {// 每一帧x和y轴移动的距离this.x += this.Vx;this.y += this.Vy;// 每一帧速度变化this.Vy += 0.15;// 每一帧清晰度较小this.opacity = this.opacity - 0.01;}draw() {ctx.beginPath();ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);ctx.fillStyle = this.color;ctx.globalAlpha = this.opacity;ctx.fill();}
}

粒子聚合成烟花

如何将粒子聚合成烟花呢,我们声明一个烟花类即可。对于一个烟花,我们只需要知道这个研发爆炸在什么位置就可以了,也就是xy的坐标。

  • 粒子的颜色? 为了让粒子的颜色更加饱和一点,我们采用的是HSL颜色,跟RGBHEX记录颜色不同,这个是用色相、饱和度和明度(HSL)来指定颜色。这里我们采用的代码为
// 是JavaScript中动态生成一个随机色相、饱和度为100%、亮度为50%的HSL颜色值。
const color = `hsl(${Math.random() * 360}, 100%, 50%)`;
  • 粒子的初始速度? 对于Vx速度,我们可以取[-x,x]的区间,允许粒子在横向轴上向左向右运动。对于Vy速度,我们这次都取向上的速度,这里向上的速度是负的,因为之前说的对于屏幕而言,右上角的px是(0,0)。
const Vx = (Math.random() - 0.5) * 6;
const Vy = -Math.random() * 10;

于是我们可以将烟花类写好

class Firework {constructor(x, y) {this.x = x;this.y = y;this.dots = [];// 一次性放40个烟花粒子for (let i = 0; i < 40; i++) {const color = `hsl(${Math.random() * 360}, 100%, 50%)`;const Vx = (Math.random() - 0.5) * 6;const Vy = -Math.random() * 10;this.dots.push(new Dot(x, y, color, Vx, Vy));}}draw() {this.dots.forEach((particle) => particle.update());this.dots.forEach((particle) => particle.draw());}
}

让粒子动起来

我们现在有这些炫酷的粒子了,如何让粒子动起来呢?没错,使用requestAnimationFrame大法。参见MDN

你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行

直接调用Fireworkdraw于是我们就能得到这样的效果。
Feb-16-2024 14-02-32.gif
但是粒子是有了,但是我们想要的是粒子有一个流星一样的尾巴,那该如何做呢?
解决: 我们将背景设为半透明的黑色,这样结合之前粒子下落时也会半透明是不是碰撞出新的视图呢?这里我们采用如下代码设置Canvas 2D 绘图上下文的填充颜色为半透明黑色

ctx.fillStyle = "rgba(0, 0, 0, 0.1)";
ctx.fillRect(0, 0, canvas.width, canvas.height);

之后,我们可以通过点击屏幕,触发这个烟花绽放。相应的代码如下

// 鼠标点击触发烟花效果
canvas.addEventListener("click", (event) => {const x = event.clientX;const y = event.clientY;// 所有的烟花fireworks.push(new Firework(x, y));
});function animate() {ctx.fillStyle = "rgba(0, 0, 0, 0.1)";ctx.fillRect(0, 0, canvas.width, canvas.height);fireworks.forEach((firework, index) => {// 去除透明度为0的烟花if (firework.dots[0].opacity <= 0) {fireworks.splice(index, 1);} else {firework.draw();}});requestAnimationFrame(animate);
}

对应的效果图如下:
Feb-16-2024 14-11-13.gif

全部代码

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>烟花效果</title><style>body {margin: 0;}</style></head><body><canvas id="fireworksCanvas"></canvas><script>const canvas = document.getElementById("fireworksCanvas");const ctx = canvas.getContext("2d");// 所有烟花的集合let fireworks = [];// 设置画布大小canvas.width = window.innerWidth;canvas.height = window.innerHeight;// 监听窗口大小变化window.addEventListener("resize", () => {canvas.width = window.innerWidth;canvas.height = window.innerHeight;});class Dot {constructor(x, y, color, Vx, Vy) {this.x = x;this.y = y;this.Vx = Vx;this.Vy = Vy;this.color = color;this.radius = 2.5;this.opacity = 1;}update() {this.x += this.Vx;this.y += this.Vy;this.Vy += 0.15;this.opacity = this.opacity - 0.01;}draw() {ctx.beginPath();ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);ctx.fillStyle = this.color;ctx.globalAlpha = this.opacity;ctx.fill();}}class Firework {constructor(x, y) {this.x = x;this.y = y;this.dots = [];for (let i = 0; i < 40; i++) {const color = `hsl(${Math.random() * 360}, 100%, 50%)`;const Vx = (Math.random() - 0.5) * 6;const Vy = -Math.random() * 10;this.dots.push(new Dot(x, y, color, Vx, Vy));}}draw() {this.dots.forEach((particle) => particle.update());this.dots.forEach((particle) => particle.draw());}}// 鼠标点击触发烟花效果canvas.addEventListener("click", (event) => {const x = event.clientX;const y = event.clientY;fireworks.push(new Firework(x, y));});function animate() {ctx.fillStyle = "rgba(0, 0, 0, 0.1)";ctx.fillRect(0, 0, canvas.width, canvas.height);fireworks.forEach((firework, index) => {if (firework.dots[0].opacity <= 0) {fireworks.splice(index, 1);} else {firework.draw();}});requestAnimationFrame(animate);}// 启动动画animate();</script></body>
</html>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/684516.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis篇----第一篇

系列文章目录 文章目录 系列文章目录前言一、什么是 Redis?二、Redis 与其他 key-value 存储有什么不同?三、Redis 的数据类型?四、使用 Redis 有哪些好处?五、Redis 相比 Memcached 有哪些优势?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住…

Spring 用法学习总结(四)之 JdbcTemplate 连接数据库

&#x1f409;目录 9 JdbcTemplate 9 JdbcTemplate Spring 框架对 JDBC 进行了封装&#xff0c;使用 JdbcTemplate 方便实现对数据库操作 相关包&#xff1a; 百度网盘链接https://pan.baidu.com/s/1Gw1l6VKc-p4gdqDyD626cg?pwd6666 创建properties配置文件 &#x1f4a5;注意…

分布式文件系统 SpringBoot+FastDFS+Vue.js【二】

分布式文件系统 SpringBootFastDFSVue.js【二】 六、实现上传功能并展示数据6.1.创建数据库6.2.创建spring boot项目fastDFS-java6.3.引入依赖6.3.fastdfs-client配置文件6.4.跨域配置GlobalCrosConfig.java6.5.创建模型--实体类6.5.1.FastDfsFile.java6.5.2.FastDfsFileType.j…

vscode 和 keil协同使用开发stm32程序,超详细教程

vscode 和 keil协同使用开发stm32程序 文章目录 vscode 和 keil协同使用开发stm32程序1. 安装vscode拓展安装chinese插件 2 .安装Mingw3.配置环境变量4. 打开Keil项目 VSCODE 是一款广受好评的代码编辑器&#xff0c; KEIL 是常用的嵌入式开发工具但编程界面简陋。 将两个工具…

npm使用国内淘宝镜像(最新地址)

目录 前言 一、命令配置 二、使用cnpm安装 三、常见包地址 四、总结 往期回顾 前言 我们前端程序员在使用国外的镜像源速度很慢并且容易下载失败&#xff0c;有时候需要尝试多次才有可能下载成功&#xff0c;很麻烦&#xff0c;但是可以切换为国内镜像源&#xff0c;下…

BigDecimal的常用API

BigDecimal用于解决浮点型运算时结果出现失真的问题。 这里0.20.1等于0.3就出现了失真 import java.math.BigDecimal; import java.math.RoundingMode;public class Test {public static void main(String[] args) {//BigDeciaml的使用&#xff1a;解决小数运算失真的问题doub…

微信网页版能够使用(会顶掉微信app的登陆)

一、文件结构 新建目录chrome新建icons&#xff0c;其中图片你自己找吧新建文件manifest.json新建文件wx-rules.json 二、文件内容 对应的png你们自己改下 1、manifest.json {"manifest_version": 3,"name": "wechat-need-web","author…

C语言第二十五弹---字符函数和字符串函数(上)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 目录 1、字符分类函数 2、字符转换函数 3、strlen的使用和模拟实现 4、strcpy 的模拟实现 5、strcat 的模拟实现 6、strcmp 的模拟实现 7、strncpy 函数的使用 总结…

Excel练习:日历

Excel练习&#xff1a;日历 ‍ 题目&#xff1a;制作日历 ‍ ​​ 用rows和columns函数计算日期单元格偏移量 一个公式填充所有日期单元格 ​​ ‍

CSP-201909-1-小明种苹果

CSP-201909-1-小明种苹果 #include <iostream> using namespace std; int main() {long long sumApple 0, maxNum 0, maxAppleNum 0, n, m;cin >> n >> m;for (long long i 0; i < n; i){long long appleNum, delta 0;cin >> appleNum;for (l…

【JavaEE】spring boot快速上手

SpringBoot快速上手 文章目录 SpringBoot快速上手Maven会出现的一个官方bug创建完项目之后常用的的三个功能依赖管理Maven仓库中央仓库本地仓库国内源配置私服 springboot项目创建什么是springspring boot项目的创建Hello Worldweb服务器 SpringMVC什么是SpringWebMVC什么是MVC…

111. 二叉树的最小深度

给定一个二叉树&#xff0c;找出其最小深度。 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 说明&#xff1a;叶子节点是指没有子节点的节点。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;2示例 2&#xff1a; 输入…

Three.js学习9:Three.js 响应式设计

-----------------------------华丽的分割线--------------------- 相关代码均已上传到 gitee 中&#xff1a;myThree: 学习 Three.js &#xff0c;努力加油~&#xff01; Gitee 静态演示地址&#xff1a;Three JS 演示页面 -----------------------------华丽的分割线------…

相机图像质量研究(17)常见问题总结:CMOS期间对成像的影响--靶面尺寸

系列文章目录 相机图像质量研究(1)Camera成像流程介绍 相机图像质量研究(2)ISP专用平台调优介绍 相机图像质量研究(3)图像质量测试介绍 相机图像质量研究(4)常见问题总结&#xff1a;光学结构对成像的影响--焦距 相机图像质量研究(5)常见问题总结&#xff1a;光学结构对成…

【AIGC】Stable Diffusion的常见错误

Stable Diffusion 在使用过程中可能会遇到各种各样的错误。以下是一些常见的错误以及可能的解决方案&#xff1a; 模型加载错误&#xff1a;可能出现模型文件损坏或缺失的情况。解决方案包括重新下载模型文件&#xff0c;确保文件完整并放置在正确的位置。 依赖项错误&#x…

009集——磁盘详解——电脑数据如何存储在磁盘

很多人也知道数据能够保存是由于设备中有一个叫做「硬盘」的组件存在&#xff0c;但也有很多人不知道硬盘是怎样储存这些数据的。这里给大家讲讲其中的原理。 首先我们要明白的是&#xff0c;计算机中只有0和1&#xff0c;那么我们存入硬盘的数据&#xff0c;实际上也就是一堆0…

Python三级考试笔记

Python三级考试笔记【源源老师】 三级标准 一、 理解编码、数制的基本概念&#xff0c;并且会应用。 1. 能够进行二进制、十进制以及十六进制之间的转换&#xff1b; 2. 理解Python中的数制转换函数。 二、 掌握一维数据的表示和读写方法&#xff0c;能够编写程序处理一维数据…

【C++】C++11上

C11上 1.C11简介2.统一的列表初始化2.1 {} 初始化2.2 initializer_list 3.变量类型推导3.1auto3.2decltype3.3nullptr 4.范围for循环5.final与override6.智能指针7. STL中一些变化8.右值引用和移动语义8.1左值引用和右值引用8.2左值引用与右值引用比较8.3右值引用使用场景和意义…

【算法设计与分析】搜索旋转排序数组

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;算法分析与设计 ⛺️稳中求进&#xff0c;晒太阳 题目 整数数组 nums 按升序排列&#xff0c;数组中的值 互不相同 。 在传递给函数之前&#xff0c;nums 在预先未知的某个下标 k&#xff…

什么是“感知机”?

感知机&#xff08;神经网络和支持向量机的理论基础&#xff09; 概念&#xff1a;简单来说&#xff0c;感知机就是一个旨在建立一个线性超平面对线性可分的数据集进行分类的线性模型 分类&#xff1a; 单层感知机多层感知机&#xff08; Multi-Layer Perceptron&#xff0c…