|
| 1 | +import tkinter as tk |
| 2 | +from time import time, sleep |
| 3 | +from PIL import Image, ImageTk |
| 4 | +from math import sin, cos, radians |
| 5 | +from random import choice, uniform, randint |
| 6 | + |
| 7 | +colors = ['red', 'blue', 'yellow', 'white', 'green', 'orange', |
| 8 | + 'purple', 'seagreen', 'indigo', 'cornflowerblue'] |
| 9 | + |
| 10 | +# 烟花类 |
| 11 | +class fireworks: |
| 12 | + def __init__(self, cv, idx, total, explosion_speed, x=0., y=0., |
| 13 | + vx=0., vy=0., size=2., color='red', lifespan=2, **kwargs): |
| 14 | + self.id = idx |
| 15 | + # 烟花绽放 x 轴 |
| 16 | + self.x = x |
| 17 | + # 烟花绽放 x 轴 |
| 18 | + self.y = y |
| 19 | + self.initial_speed = explosion_speed |
| 20 | + # 外放 x 轴速度 |
| 21 | + self.vx = vx |
| 22 | + # 外放 y 轴速度 |
| 23 | + self.vy = vy |
| 24 | + # 绽放的粒子数 |
| 25 | + self.total = total |
| 26 | + # 已停留时间 |
| 27 | + self.age = 0 |
| 28 | + # 颜色 |
| 29 | + self.color = color |
| 30 | + # 画布 |
| 31 | + self.cv = cv |
| 32 | + self.cid = self.cv.create_oval(x - size, y - size, x + size, y + size, |
| 33 | + fill=self.color) |
| 34 | + self.lifespan = lifespan |
| 35 | + |
| 36 | + # 更新数据 |
| 37 | + def update(self, dt): |
| 38 | + self.age += dt |
| 39 | + # 粒子膨胀 |
| 40 | + if self.alive() and self.expand(): |
| 41 | + move_x = cos(radians(self.id * 360 / self.total)) * self.initial_speed |
| 42 | + move_y = sin(radians(self.id * 360 / self.total)) * self.initial_speed |
| 43 | + self.cv.move(self.cid, move_x, move_y) |
| 44 | + self.vx = move_x / (float(dt) * 1000) |
| 45 | + # 膨胀到最大下落 |
| 46 | + elif self.alive(): |
| 47 | + move_x = cos(radians(self.id * 360 / self.total)) |
| 48 | + self.cv.move(self.cid, self.vx + move_x, self.vy + 0.5 * dt) |
| 49 | + self.vy += 0.5 * dt |
| 50 | + # 过期移除 |
| 51 | + elif self.cid is not None: |
| 52 | + cv.delete(self.cid) |
| 53 | + self.cid = None |
| 54 | + |
| 55 | + # 定义膨胀效果的时间帧 |
| 56 | + def expand(self): |
| 57 | + return self.age <= 1.5 |
| 58 | + |
| 59 | + # 检查粒子是否仍在生命周期内 |
| 60 | + def alive(self): |
| 61 | + return self.age <= self.lifespan |
| 62 | + |
| 63 | +# 燃放烟花 |
| 64 | +def ignite(cv): |
| 65 | + t = time() |
| 66 | + # 烟花列表 |
| 67 | + explode_points = [] |
| 68 | + wait_time = randint(10, 100) |
| 69 | + # 爆炸的个数 |
| 70 | + numb_explode = randint(6, 10) |
| 71 | + for point in range(numb_explode): |
| 72 | + # 爆炸粒子列表 |
| 73 | + objects = [] |
| 74 | + # 爆炸 x 轴 |
| 75 | + x_cordi = randint(50, 550) |
| 76 | + # 爆炸 y 轴 |
| 77 | + y_cordi = randint(50, 150) |
| 78 | + speed = uniform(0.5, 1.5) |
| 79 | + size = uniform(0.5, 3) |
| 80 | + color = choice(colors) |
| 81 | + # 爆炸的绽放速度 |
| 82 | + explosion_speed = uniform(0.2, 1) |
| 83 | + # 爆炸的粒子数半径 |
| 84 | + total_particles = randint(10, 50) |
| 85 | + for i in range(1, total_particles): |
| 86 | + r = fireworks(cv, idx=i, total=total_particles, |
| 87 | + explosion_speed=explosion_speed, x=x_cordi, y=y_cordi, |
| 88 | + vx=speed, vy=speed, color=color, size=size, |
| 89 | + lifespan=uniform(0.6, 1.75)) |
| 90 | + # 添加进粒子列表里 |
| 91 | + objects.append(r) |
| 92 | + # 把粒子列表添加到烟花列表 |
| 93 | + explode_points.append(objects) |
| 94 | + total_time = .0 |
| 95 | + # 在 1.8 秒时间帧内保持更新 |
| 96 | + while total_time < 1.8: |
| 97 | + # 让画面暂停 0.01s |
| 98 | + sleep(0.01) |
| 99 | + # 刷新时间 |
| 100 | + tnew = time() |
| 101 | + t, dt = tnew, tnew - t |
| 102 | + # 遍历烟花列表 |
| 103 | + for point in explode_points: |
| 104 | + # 遍历烟花里的粒子列表 |
| 105 | + for item in point: |
| 106 | + # 更新时间 |
| 107 | + item.update(dt) |
| 108 | + # 刷新页面 |
| 109 | + cv.update() |
| 110 | + total_time += dt |
| 111 | + root.after(wait_time, ignite, cv) |
| 112 | + |
| 113 | +def close(*ignore): |
| 114 | + global root |
| 115 | + root.quit() |
| 116 | + |
| 117 | +if __name__ == "__main__": |
| 118 | + root = tk.Tk() |
| 119 | + # 绘制一个画布 |
| 120 | + cv = tk.Canvas(root, height=457, width=690) |
| 121 | + # 背景图 |
| 122 | + image = Image.open("bg.jpeg") |
| 123 | + photo = ImageTk.PhotoImage(image) |
| 124 | + # 在画板上绘制一张图片 |
| 125 | + cv.create_image(0, 0, image=photo, anchor='nw') |
| 126 | + cv.pack() |
| 127 | + root.protocol(close) |
| 128 | + root.after(100, ignite, cv) |
| 129 | + # # 生成窗口 |
| 130 | + root.mainloop() |
0 commit comments