Python实现烟花特效
import tkinter as tk
from tkinter import Canvas
from time import (
time,
sleep
)
from typing import (
List,
Union
)
from random import (
choice,
uniform,
randint
)
from math import (
sin,
cos,
radians
)
from PIL import (
Image,
ImageTk
)
GRAVITY: float = 0.05
colors: List[str] = [
'red',
'blue',
'yellow',
'white',
'green',
'orange',
'purple',
'seagreen',
'indigo'
]
class Particle:
"""
粒子
粒子在空中随机生成随机,变成一个圈、下坠、消失
"""
def __init__(
self,
idx: int,
total: int,
canvas: Canvas,
explosion_speed: float,
x: float = 0.,
y: float = 0.,
size: float = 2.,
color: str = 'red',
speed_x: float = 0.,
speed_y: float = 0.,
max_live_time: int = 3
):
"""
粒子类初始化
:param idx: 粒子的id
:param total: 粒子总数
:param canvas: 画布实例
:param explosion_speed: 爆炸速度
:param x: 粒子的横坐标
:param y: 粒子的纵坐标
:param size: 粒子大小
:param color: 粒子颜色
:param speed_x: 横坐标的扩散速度
:param speed_y: 纵坐标的扩散速度
:param max_live_time: 最大存活时间
"""
self.x = x
self.y = y
self.id = idx
self.live_time = 0
self.total = total
self.color = color
self.canvas = canvas
self.speed_x = speed_x
self.speed_y = speed_y
self.expand_time = 1.2
self.max_live_time = max_live_time
self.initial_speed = explosion_speed
self.cid = self.canvas.create_oval(
x - size,
y - size,
x + size,
y + size,
fill=self.color
)
def update(self, dt: Union[int, float]) -> None:
"""
更新粒子
:param dt: 已用时间
:return: None
"""
self.live_time += dt
if self.alive() and self.expand():
move_x = cos(radians(self.id * 360 / self.total)) * self.initial_speed
move_y = sin(radians(self.id * 360 / self.total)) * self.initial_speed
self.canvas.move(self.cid, move_x, move_y)
self.speed_x = move_x / (float(dt) * 1000)
elif self.alive():
move_x = cos(radians(self.id * 360 / self.total))
self.canvas.move(self.cid, self.speed_x + move_x, self.speed_y + GRAVITY * dt)
self.speed_y += GRAVITY * dt
elif self.cid is not None:
self.canvas.delete(self.cid)
self.cid = None
def expand(self) -> bool:
"""
判断粒子是否要继续扩大(当前存活时间是否超过扩大时间)
:return: True / False
"""
return self.live_time <= self.expand_time
def alive(self) -> bool:
"""
判断粒子是否存活(当前存活时间是否超过最大存活时间)
:return: True / False
"""
return self.live_time <= self.max_live_time
'''
循环调用保持不停
'''
def simulate(canvas) -> None:
"""
模拟放烟花
:param canvas: 画布实例
:return: None
"""
t: float = time()
wait_time: int = randint(10, 100)
numb_explode: int = randint(6, 10)
explode_points: List[List[Particle]] = []
for point in range(numb_explode):
size: float = uniform(0.5, 3)
color: str = choice(colors)
speed: float = uniform(0.5, 1.5)
x_cordi: int = randint(50, 550)
y_cordi: int = randint(50, 150)
objects: List[Particle] = []
total_particles: int = randint(10, 50)
explosion_speed: float = uniform(0.2, 1)
for i in range(1, total_particles):
particle_instance: Particle = Particle(
idx=i,
x=x_cordi,
y=y_cordi,
size=size,
color=color,
speed_x=speed,
speed_y=speed,
canvas=canvas,
total=total_particles,
explosion_speed=explosion_speed,
max_live_time=uniform(0.6, 1.75),
)
objects.append(particle_instance)
explode_points.append(objects)
total_time = .0
while total_time < 3:
sleep(0.01)
t_new: float = time()
t, dt = t_new, t_new - t
for point in explode_points:
for item in point:
item.update(dt)
canvas.update()
total_time += dt
root.after(wait_time, simulate, canvas)
def close():
"""退出程序、关闭窗口"""
global root
root.quit()
if __name__ == '__main__':
root = tk.Tk()
canvas_instance: Canvas = Canvas(root, width=400, height=300)
image = Image.open('./City.jpg')
photo = ImageTk.PhotoImage(image)
canvas_instance.create_image(0, 0, image=photo, anchor='nw')
canvas_instance.pack()
root.protocol('Firework', close)
root.after(100, simulate, canvas_instance)
root.mainloop()