用户注册



邮箱:

密码:

用户登录


邮箱:

密码:
记住登录一个月忘记密码?

发表随想


还能输入:200字
云代码 - python代码库

烟花

2022-03-07 作者: 星际举报

[python]代码库

# -*- coding: utf-8 -*-

import math, random, time
import threading
import tkinter as tk
import re

# import uuid

Fireworks = []
maxFireworks = 8
height, width = 600, 600


class firework(object):
    def __init__(self, color, speed, width, height):
        # uid=uuid.uuid1()
        self.radius = random.randint(2, 4)  # 粒子半径为2~4像素
        self.color = color  # 粒子颜色
        self.speed = speed  # speed是1.5-3.5秒
        self.status = 0  # 在烟花未爆炸的情况下,status=0;爆炸后,status>=1;当status>100时,烟花的生命期终止
        self.nParticle = random.randint(20, 30)  # 粒子数量
        self.center = [random.randint(0, width - 1), random.randint(0, height - 1)]  # 烟花随机中心坐标
        self.oneParticle = []  # 原始粒子坐标(100%状态时)
        self.rotTheta = random.uniform(0, 2 * math.pi)  # 椭圆平面旋转角

        # 椭圆参数方程:x=a*cos(theta),y=b*sin(theta)
        # ellipsePara=[a,b]

        self.ellipsePara = [random.randint(30, 40), random.randint(20, 30)]
        theta = 2 * math.pi / self.nParticle
        for i in range(self.nParticle):
            t = random.uniform(-1.0 / 16, 1.0 / 16)  # 产生一个 [-1/16,1/16) 的随机数
            x, y = self.ellipsePara[0] * math.cos(theta * i + t), self.ellipsePara[1] * math.sin(
                theta * i + t)  # 椭圆参数方程
            xx, yy = x * math.cos(self.rotTheta) - y * math.sin(self.rotTheta), y * math.cos(
                self.rotTheta) + x * math.sin(self.rotTheta)  # 平面旋转方程
            self.oneParticle.append([xx, yy])

        self.curParticle = self.oneParticle[0:]  # 当前粒子坐标
        self.thread = threading.Thread(target=self.extend)  # 建立线程对象

    def extend(self):  # 粒子群状态变化函数线程
        for i in range(100):
            self.status += 1  # 更新状态标识
            self.curParticle = [[one[0] * self.status / 100, one[1] * self.status / 100] for one in
                                self.oneParticle]  # 更新粒子群坐标
            time.sleep(self.speed / 50)

    def explode(self):
        self.thread.setDaemon(True)  # 把现程设为守护线程
        self.thread.start()  # 启动线程

    def __repr__(self):
        return ('color:{color}\n'
                'speed:{speed}\n'
                'number of particle: {np}\n'
                'center:[{cx} , {cy}]\n'
                'ellipse:a={ea} , b={eb}\n'
                'particle:\n{p}\n'
                ).format(color=self.color, speed=self.speed, np=self.nParticle, cx=self.center[0], cy=self.center[1],
                         p=str(self.oneParticle), ea=self.ellipsePara[0], eb=self.ellipsePara[1])


def colorChange(fire):
    rgb = re.findall(r'(.{2})', fire.color[1:])
    cs = fire.status

    f = lambda x, c: hex(int(int(x, 16) * (100 - c) / 30))[2:]  # 当粒子寿命到70%时,颜色开始线性衰减
    if cs > 70:
        ccr, ccg, ccb = f(rgb[0], cs), f(rgb[1], cs), f(rgb[2], cs)
    else:
        ccr, ccg, ccb = rgb[0], rgb[1], rgb[2]

    return '#{0:0>2}{1:0>2}{2:0>2}'.format(ccr, ccg, ccb)


def appendFirework(n=1):  # 递归生成烟花对象
    if n > maxFireworks or len(Fireworks) > maxFireworks:
        pass
    elif n == 1:
        cl = '#{0:0>6}'.format(hex(int(random.randint(0, 16777215)))[2:])  # 产生一个0~16777215(0xFFFFFF)的随机数,作为随机颜色
        a = firework(cl, random.uniform(1.5, 3.5), width, height)
        Fireworks.append({'particle': a, 'points': []})  # 建立粒子显示列表,‘particle’为一个烟花对象,‘points’为每一个粒子显示时的对象变量集
        a.explode()
    else:
        appendFirework()
        appendFirework(n - 1)


def show(c):
    for p in Fireworks:  # 每次刷新显示,先把已有的所以粒子全部删除
        for pp in p['points']:
            c.delete(pp)

    for p in Fireworks:  # 根据每个烟花对象,计算其中每个粒子的显示对象
        oneP = p['particle']
        if oneP.status == 100:  # 状态标识为100,说明烟花寿命结束
            Fireworks.remove(p)  # 移出当前烟花
            appendFirework()  # 新增一个烟花
            continue
        else:
            li = [[int(cp[0] * 2) + oneP.center[0], int(cp[1] * 2) + oneP.center[1]] for cp in
                  oneP.curParticle]  # 把中心为原点的椭圆平移到随机圆心坐标上
            color = colorChange(oneP)  # 根据烟花当前状态计算当前颜色
            for pp in li:
                p['points'].append(
                    c.create_oval(pp[0] - oneP.radius, pp[1] - oneP.radius, pp[0] + oneP.radius, pp[1] + oneP.radius,
                                  fill=color))  # 绘制烟花每个粒子

    root.after(50, show, c)  # 回调,每50ms刷新一次


if __name__ == '__main__':
    appendFirework(maxFireworks)

    root = tk.Tk()
    cv = tk.Canvas(root, height=height, width=width)
    cv.create_rectangle(0, 0, width, height, fill="black")

    cv.pack()

    root.after(50, show, cv)
    root.mainloop()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
在这里插入图片描述

import tkinter as tk
from PIL import Image, ImageTk
from time import time, sleep
from random import choice, uniform, randint
from math import sin, cos, radians

# 模拟重力
GRAVITY = 0.05
# 颜色选项(随机或者按顺序)
colors = ['red', 'blue', 'yellow', 'white', 'green', 'orange', 'purple', 'seagreen', 'indigo', 'cornflowerblue']

'''
particles 类
粒子在空中随机生成随机,变成一个圈、下坠、消失
属性:
    - id: 粒子的id
    - x, y: 粒子的坐标
    - vx, vy: 在坐标的变化速度
    - total: 总数
    - age: 粒子存在的时长
    - color: 颜色
    - cv: 画布
    - lifespan: 最高存在时长
'''


class Particle:

    def __init__(self, cv, idx, total, explosion_speed, x=0., y=0., vx=0., vy=0., size=2., color='red', lifespan=2,
                 **kwargs):
        self.id = idx
        self.x = x
        self.y = y
        self.initial_speed = explosion_speed
        self.vx = vx
        self.vy = vy
        self.total = total
        self.age = 0
        self.color = color
        self.cv = cv
        self.cid = self.cv.create_oval(
            x - size, y - size, x + size,
            y + size, fill=self.color)
        self.lifespan = lifespan

    def update(self, dt):
        self.age += 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.cv.move(self.cid, move_x, move_y)
            self.vx = move_x / (float(dt) * 1000)

        # 以自由落体坠落
        elif self.alive():
            move_x = cos(radians(self.id * 360 / self.total))
            # we technically don't need to update x, y because move will do the job
            self.cv.move(self.cid, self.vx + move_x, self.vy + GRAVITY * dt)
            self.vy += GRAVITY * dt

        # 移除超过最高时长的粒子
        elif self.cid is not None:
            cv.delete(self.cid)
            self.cid = None

    # 扩大的时间
    def expand(self):
        return self.age <= 1.2

    # 粒子是否在最高存在时长内
    def alive(self):
        return self.age <= self.lifespan


'''
循环调用保持不停
'''


def simulate(cv):
    t = time()
    explode_points = []
    wait_time = randint(10, 100)
    numb_explode = randint(6, 10)
    # 创建一个所有粒子同时扩大的二维列表
    for point in range(numb_explode):
        objects = []
        x_cordi = randint(50, 550)
        y_cordi = randint(50, 150)
        speed = uniform(0.5, 1.5)
        size = uniform(0.5, 3)
        color = choice(colors)
        explosion_speed = uniform(0.2, 1)
        total_particles = randint(10, 50)
        for i in range(1, total_particles):
            r = Particle(cv, idx=i, total=total_particles, explosion_speed=explosion_speed, x=x_cordi, y=y_cordi,
                         vx=speed, vy=speed, color=color, size=size, lifespan=uniform(0.6, 1.75))
            objects.append(r)
        explode_points.append(objects)

    total_time = .0
    # 1.8s内一直扩大
    while total_time < 1.8:
        sleep(0.01)
        tnew = time()
        t, dt = tnew, tnew - t
        for point in explode_points:
            for item in point:
                item.update(dt)
        cv.update()
        total_time += dt
    # 循环调用
    root.after(wait_time, simulate, cv)


def close(*ignore):
    """退出程序、关闭窗口"""
    global root
    root.quit()


if __name__ == '__main__':
    root = tk.Tk()
    cv = tk.Canvas(root, height=1200, width=800)
    # 选一个好看的背景会让效果更惊艳!
    image = Image.open("./image.png")
    photo = ImageTk.PhotoImage(image)

    cv.create_image(0, 0, image=photo, anchor='nw')
    cv.pack()

    root.protocol("WM_DELETE_WINDOW", close)
    root.after(100, simulate, cv)
    root.mainloop()

[代码运行效果截图]


烟花


网友评论    (发表评论)


发表评论:

评论须知:

  • 1、评论每次加2分,每天上限为30;
  • 2、请文明用语,共同创建干净的技术交流环境;
  • 3、若被发现提交非法信息,评论将会被删除,并且给予扣分处理,严重者给予封号处理;
  • 4、请勿发布广告信息或其他无关评论,否则将会删除评论并扣分,严重者给予封号处理。


扫码下载

加载中,请稍后...

输入口令后可复制整站源码

加载中,请稍后...