<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Fireworks</title>
<style>
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
background-color: black;
overflow: hidden;
}
canvas {
display: block;
margin: auto;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var width = window.innerWidth;
var height = window.innerHeight;
canvas.width = width;
canvas.height = height;
// 烟花的构造函数
function Firework(x, y) {
this.x = x;
this.y = y;
this.size = 2;
var random = Math.random();
this.speed = random * 4 + 8;
this.angle = 1.25;
this.vx = Math.cos(this.angle) * this.speed;
this.vy = Math.sin(this.angle) * this.speed;
this.gravity = 0.1;
this.opacity = 1;
this.particles = [];
this.isExploded = false;
}
Firework.prototype.update = function() {
if (!this.isExploded) {
this.x += this.vx;
this.y -= this.vy + this.gravity;
if (this.y < height * 0.3) {
this.explode();
}
} else {
this.particles.forEach(function(particle) {
particle.vx *= particle.friction;
particle.vy *= particle.friction;
particle.vy += particle.gravity;
particle.x += particle.vx;
particle.y += particle.vy;
particle.opacity -= 0.01;
});
this.particles = this.particles.filter(function(particle) {
return particle.opacity > 0;
});
if (this.particles.length === 0) {
this.isExploded = false;
}
}
};
Firework.prototype.draw = function() {
if (!this.isExploded) {
ctx.beginPath();
//ctx.fillStyle = this.color;
ctx.globalAlpha = this.opacity;
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2, false);
ctx.closePath();
ctx.fill();
} else {
this.particles.forEach(function(particle) {
ctx.beginPath();
ctx.fillStyle = particle.color;
ctx.globalAlpha = particle.opacity;
ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2, false);
ctx.closePath();
ctx.fill();
});
}
};
Firework.prototype.explode = function() {
for (var i = 0; i < 50; i++) {
var particle = new Particle(this.x, this.y);
this.particles.push(particle);
}
this.isExploded = true;
};
// 烟花颜色数组
var colors = ["#FF4136", "#0074D9", "#2ECC40", "#FFDC00", "#7FDBFF", "#FF851B", "#B10DC9"];
// 粒子的构造函数
function Particle(x, y) {
this.x = x;
this.y = y;
this.color = colors[Math.floor(Math.random() * colors.length)];
this.size = 3;
this.speed = Math.random() * 4 + 2;
this.angle = Math.random() * Math.PI * 2;
this.vx = Math.cos(this.angle) * this.speed;
this.vy = Math.sin(this.angle) * this.speed;
this.gravity = 0.2;
this.friction = 0.95;
this.opacity = 1;
}
// 创建烟花并添加到数组中
var fireworks = [];
setInterval(function() {
var x = Math.random() * width;
var random = Math.random();
var y = height * (random > 0.8 ? random : 0.85);
var firework = new Firework(x, y);
fireworks.push(firework);
}, 1000);
// 更新和绘制烟花
function loop() {
ctx.fillStyle = "rgba(0, 0, 0, 0.1)";
ctx.fillRect(0, 0, width, height);
fireworks.forEach(function(firework, index) {
firework.update();
firework.draw();
if (firework.isExploded && firework.particles.length === 0) {
fireworks.splice(index, 1);
}
});
requestAnimationFrame(loop);
}
loop();
</script>
</body>
</html>
- 在
Firework
的构造函数中,增加了speed
、angle
、vx
和vy
等属性,用来控制烟花的飞行方向和速度。 - 在
Firework
的update
方法中,增加了isExploded
属性,用来表示烟花是否已经爆炸。如果没有爆炸,烟花会继续向上飞行;如果已经爆炸,烟花就会变成一堆粒子,每个粒子会向各个方向飞行。 - 在
Firework
的draw
方法中,增加了对粒子的绘制。 - 在
Particle
的构造函数中,增加了friction
属性,用来控制粒子的速度衰减。 - 在
colors
数组中添加了多个烟花颜色。 - 在
setInterval
中创建烟花的代码中,将x
的取值范围改为了整个窗口的宽度,而将y
的取值范围改为了窗口高度的 80%,这样烟花就会往上飞一段距离后再爆炸。同时,还随机选择一种颜色。 - 在
loop
函数中,增加了对烟花是否已经爆炸和粒子数量是否为 0 的判断,如果是,就将该烟花从数组中删除,以减少内存占用。