Python实现烟花特效

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
 
    # 3s内一直扩大
    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()

猜你喜欢

转载自blog.csdn.net/weixin_45014634/article/details/143284554