- python版本
```python
import pygame
import random
import math
vector = pygame.math.Vector2
gravity = vector(0, 0.3)
DISPLAY_WIDTH = DISPLAY_HEIGHT = 800
trail_colours = [(45, 45, 45), (60, 60, 60), (75, 75, 75), (125, 125, 125), (150, 150, 150)]
dynamic_offset = 1
static_offset = 3
MAX_FIREWORKS = 200
MAX_PARTICLES = 5000
class Firework:
def __init__(self):
self.colour = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
self.colours = (
(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)),
(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)),
(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)))
self.firework = Particle(random.randint(0, DISPLAY_WIDTH), DISPLAY_HEIGHT, True,
self.colour)
self.exploded = False
self.particles = []
self.min_max_particles = (100, 225)
def update(self, win):
if not self.exploded:
self.firework.apply_force(gravity)
self.firework.move()
for tf in self.firework.trails:
tf.show(win)
self.show(win)
if self.firework.vel.y >= 0:
self.exploded = True
self.explode()
else:
for particle in self.particles:
particle.apply_force(vector(gravity.x + random.uniform(-1, 1) / 20, gravity.y / 2 + (random.randint(1, 8) / 100)))
particle.move()
for t in particle.trails:
t.show(win)
particle.show(win)
def explode(self):
amount = random.randint(*self.min_max_particles)
for i in range(amount):
self.particles.append(Particle(self.firework.pos.x, self.firework.pos.y, False, self.colours))
def show(self, win):
pygame.draw.circle(win, self.colour, (int(self.firework.pos.x), int(self.firework.pos.y)), self.firework.size)
def remove(self):
if self.exploded:
for p in self.particles:
if p.remove is True:
self.particles.remove(p)
if len(self.particles) == 0:
return True
else:
return False
class Particle:
def __init__(self, x, y, firework, colour):
self.firework = firework
self.pos = vector(x, y)
self.origin = vector(x, y)
self.radius = 20
self.remove = False
self.explosion_radius = random.randint(5, 18)
self.life = 0
self.acc = vector(0, 0)
self.trails = []
self.prev_posx = [-10] * 10
self.prev_posy = [-10] * 10
if self.firework:
self.vel = vector(0, -random.randint(17, 20))
self.size = 5
self.colour = colour
for i in range(5):
self.trails.append(Trail(i, self.size, True))
else:
self.vel = vector(random.uniform(-1, 1), random.uniform(-1, 1))
self.vel.x *= random.randint(3, self.explosion_radius)
self.vel.y *= random.randint(3, self.explosion_radius)
self.size = random.randint(2, 4)
self.colour = random.choice(colour)
for i in range(5):
self.trails.append(Trail(i, self.size, False))
def apply_force(self, force):
self.acc += force
def move(self):
if not self.firework:
self.vel.x *= 0.9
self.vel.y *= 0.9
self.vel += self.acc
self.pos += self.vel
self.acc *= 0
if self.life == 0 and not self.firework:
distance = math.sqrt((self.pos.x - self.origin.x) ** 2 + (self.pos.y - self.origin.y) ** 2)
if distance > self.explosion_radius:
self.remove = True
if self.vel.length() < 0.5:
self.remove = True
self.decay()
self.trail_update()
self.life += 1
def show(self, win):
pygame.draw.circle(win, (self.colour[0], self.colour[1], self.colour[2], 0), (int(self.pos.x), int(self.pos.y)),
self.size)
def decay(self):
if 50 > self.life > 10:
ran = random.randint(0, 30)
if ran == 0:
self.remove = True
elif self.life > 50:
ran = random.randint(0, 5)
if ran == 0:
self.remove = True
def trail_update(self):
self.prev_posx.pop()
self.prev_posx.insert(0, int(self.pos.x))
self.prev_posy.pop()
self.prev_posy.insert(0, int(self.pos.y))
for n, t in enumerate(self.trails):
if t.dynamic:
t.get_pos(self.prev_posx[n + dynamic_offset], self.prev_posy[n + dynamic_offset])
else:
t.get_pos(self.prev_posx[n + static_offset], self.prev_posy[n + static_offset])
class Trail:
def __init__(self, n, size, dynamic):
self.pos_in_line = n
self.pos = vector(-10, -10)
self.dynamic = dynamic
if self.dynamic:
self.colour = trail_colours[n]
self.size = int(size - n / 2)
else:
self.colour = (255, 255, 200)
self.size = size - 2
if self.size < 0:
self.size = 0
def get_pos(self, x, y):
self.pos = vector(x, y)
def show(self, win):
pygame.draw.circle(win, self.colour, (int(self.pos.x), int(self.pos.y)), self.size)
def update(win, fireworks):
to_remove = []
for fw in fireworks:
fw.update(win)
if fw.remove():
to_remove.append(fw)
for fw in to_remove:
fireworks.remove(fw)
pygame.display.update()
def main():
pygame.init()
pygame.font.init()
pygame.display.set_caption("HI")
win = pygame.display.set_mode((DISPLAY_WIDTH, DISPLAY_HEIGHT))
clock = pygame.time.Clock()
fireworks = [Firework() for i in range(2)]
running = True
while running:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_1:
if len(fireworks) < MAX_FIREWORKS:
fireworks.append(Firework())
if event.key == pygame.K_2:
for i in range(10):
if len(fireworks) < MAX_FIREWORKS:
fireworks.append(Firework())
if event.key == pygame.K_3:
for i in range(100):
if len(fireworks) < MAX_FIREWORKS:
fireworks.append(Firework())
win.fill((20, 20, 30))
if random.randint(0, 20) == 1 and len(fireworks) < MAX_FIREWORKS:
fireworks.append(Firework())
total_particles = sum(len(fw.particles) for fw in fireworks)
if total_particles > MAX_PARTICLES:
for fw in fireworks:
fw.particles = fw.particles[:MAX_PARTICLES // len(fireworks)]
update(win, fireworks)
pygame.quit()
quit()
if __name__ == '__main__':
main()
```
- javascript版本
```javascript
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
document.body.appendChild(canvas);
const gravity = { x: 0, y: 0.3 };
const DISPLAY_WIDTH = 800;
const DISPLAY_HEIGHT = 800;
const trail_colours = [
[45, 45, 45],
[60, 60, 60],
[75, 75, 75],
[125, 125, 125],
[150, 150, 150]
];
const dynamic_offset = 1;
const static_offset = 3;
const MAX_FIREWORKS = 200;
const MAX_PARTICLES = 5000;
class Firework {
constructor() {
this.colour = [
Math.floor(Math.random() * 256),
Math.floor(Math.random() * 256),
Math.floor(Math.random() * 256)
];
this.colours = [
[
Math.floor(Math.random() * 256),
Math.floor(Math.random() * 256),
Math.floor(Math.random() * 256)
],
[
Math.floor(Math.random() * 256),
Math.floor(Math.random() * 256),
Math.floor(Math.random() * 256)
],
[
Math.floor(Math.random() * 256),
Math.floor(Math.random() * 256),
Math.floor(Math.random() * 256)
]
];
this.firework = new Particle(
Math.floor(Math.random() * DISPLAY_WIDTH),
DISPLAY_HEIGHT,
true,
this.colour
);
this.exploded = false;
this.particles = [];
this.min_max_particles = [100, 225];
}
update() {
if (!this.exploded) {
this.firework.apply_force(gravity);
this.firework.move();
this.firework.show();
if (this.firework.vel.y >= 0) {
this.exploded = true;
this.explode();
}
} else {
for (const particle of this.particles) {
particle.apply_force({
x: gravity.x + (Math.random() - 0.5) / 20,
y: gravity.y / 2 + (Math.floor(Math.random() * 8) + 1) / 100
});
particle.move();
particle.show();
}
}
}
explode() {
const amount = Math.floor(
Math.random() * (this.min_max_particles[1] - this.min_max_particles[0] + 1) +
this.min_max_particles[0]
);
for (let i = 0; i < amount; i++) {
this.particles.push(
new Particle(this.firework.pos.x, this.firework.pos.y, false, this.colours)
);
}
}
remove() {
if (this.exploded) {
this.particles = this.particles.filter((p) => !p.remove);
return this.particles.length === 0;
}
return false;
}
}
class Particle {
constructor(x, y, firework, colour) {
this.firework = firework;
this.pos = { x, y };
this.origin = { x, y };
this.radius = 20;
this.remove = false;
this.explosion_radius = Math.floor(Math.random() * 14) + 5;
this.life = 0;
this.acc = { x: 0, y: 0 };
this.trails = [];
this.prev_posx = Array(10).fill(-10);
this.prev_posy = Array(10).fill(-10);
if (this.firework) {
this.vel = { x: 0, y: -Math.floor(Math.random() * 4) - 17 };
this.size = 5;
this.colour = colour;
for (let i = 0; i < 5; i++) {
this.trails.push(new Trail(i, this.size, true));
}
} else {
this.vel = {
x: (Math.random() - 0.5) * 2 * Math.floor(Math.random() * this.explosion_radius),
y: (Math.random() - 0.5) * 2 * Math.floor(Math.random() * this.explosion_radius)
};
this.size = Math.floor(Math.random() * 3) + 2;
this.colour = colour[Math.floor(Math.random() * colour.length)];
for (let i = 0; i < 5; i++) {
this.trails.push(new Trail(i, this.size, false));
}
}
}
apply_force(force) {
this.acc.x += force.x;
this.acc.y += force.y;
}
move() {
if (!this.firework) {
this.vel.x *= 0.9;
this.vel.y *= 0.9;
}
this.vel.x += this.acc.x;
this.vel.y += this.acc.y;
this.pos.x += this.vel.x;
this.pos.y += this.vel.y;
this.acc.x = 0;
this.acc.y = 0;
if (this.life === 0 && !this.firework) {
const distance = Math.sqrt(
(this.pos.x - this.origin.x) ** 2 + (this.pos.y - this.origin.y) ** 2
);
if (distance > this.explosion_radius) {
this.remove = true;
}
}
if (Math.abs(this.vel.x) < 0.5 && Math.abs(this.vel.y) < 0.5) {
this.remove = true;
}
this.decay();
this.trail_update();
this.life++;
}
show() {
ctx.beginPath();
ctx.arc(this.pos.x, this.pos.y, this.size, 0, Math.PI * 2);
ctx.fillStyle = `rgba(${this.colour[0]}, ${this.colour[1]}, ${this.colour[2]}, 1)`;
ctx.fill();
}
decay() {
if (this.life > 10 && this.life < 50 && Math.random() < 0.03) {
this.remove = true;
} else if (this.life >= 50 && Math.random() < 0.2) {
this.remove = true;
}
}
trail_update() {
this.prev_posx.pop();
this.prev_posx.unshift(this.pos.x);
this.prev_posy.pop();
this.prev_posy.unshift(this.pos.y);
for (let n = 0; n < this.trails.length; n++) {
const t = this.trails[n];
if (t.dynamic) {
t.get_pos(this.prev_posx[n + dynamic_offset], this.prev_posy[n + dynamic_offset]);
} else {
t.get_pos(this.prev_posx[n + static_offset], this.prev_posy[n + static_offset]);
}
}
}
}
class Trail {
constructor(n, size, dynamic) {
this.pos_in_line = n;
this.pos = { x: -10, y: -10 };
this.dynamic = dynamic;
if (this.dynamic) {
this.colour = `rgb(${trail_colours[n][0]}, ${trail_colours[n][1]}, ${trail_colours[n][2]})`;
this.size = size - n / 2;
} else {
this.colour = 'rgb(255, 255, 200)';
this.size = size - 2;
if (this.size < 0) {
this.size = 0;
}
}
}
get_pos(x, y) {
this.pos = { x, y };
}
show() {
ctx.beginPath();
ctx.arc(this.pos.x, this.pos.y, this.size, 0, Math.PI * 2);
ctx.fillStyle = this.colour;
ctx.fill();
}
}
function update(fireworks) {
const to_remove = [];
fireworks.forEach((fw) => {
fw.update();
if (fw.remove()) {
to_remove.push(fw);
}
});
to_remove.forEach((fw) => {
const index = fireworks.indexOf(fw);
fireworks.splice(index, 1);
});
}
function main() {
canvas.width = DISPLAY_WIDTH;
canvas.height = DISPLAY_HEIGHT;
const fireworks = Array.from({ length: 2 }, () => new Firework());
setInterval(() => {
if (fireworks.length < MAX_FIREWORKS && Math.random() < 0.05) {
fireworks.push(new Firework());
}
}, 100);
setInterval(() => {
if (fireworks.length < MAX_FIREWORKS && Math.random() < 0.1) {
for (let i = 0; i < 10; i++) {
if (fireworks.length < MAX_FIREWORKS) {
fireworks.push(new Firework());
}
}
}
}, 500);
setInterval(() => {
if (fireworks.length < MAX_FIREWORKS && Math.random() < 0.2) {
for (let i = 0; i < 100; i++) {
if (fireworks.length < MAX_FIREWORKS) {
fireworks.push(new Firework());
}
}
}
}, 1000);
function animate() {
ctx.fillStyle = 'rgba(20, 20, 30, 0.2)';
ctx.fillRect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT);
update(fireworks);
requestAnimationFrame(animate);
}
animate();
}
main();
```