1+ import random
2+ from math import sin , cos , pi , log
3+ from tkinter import *
4+
5+ # 画布的宽
6+ CANVAS_WIDTH = 840
7+ # 画布的高
8+ CANVAS_HEIGHT = 680
9+ # 画布中心的X轴坐标
10+ CANVAS_CENTER_X = CANVAS_WIDTH / 2
11+ # 画布中心的Y轴坐标
12+ CANVAS_CENTER_Y = CANVAS_HEIGHT / 2
13+ # 放大比例
14+ IMAGE_ENLARGE = 11
15+
16+ HEART_COLOR = "#EEAEEE"
17+
18+ """
19+ 爱心函数生成器
20+ -shrink_ratio: 放大比例
21+ -t: 参数
22+ """
23+ def heart_function (t , shrink_ratio : float = IMAGE_ENLARGE ):
24+ # 基础函数
25+ x = 17 * (sin (t ) ** 3 )
26+ y = - (16 * cos (t ) - 5 * cos (2 * t ) - 2 * cos (3 * t ) - cos (3 * t ))
27+
28+ # 放大
29+ x *= IMAGE_ENLARGE
30+ y *= IMAGE_ENLARGE
31+ # 移到画布中央
32+ x += CANVAS_CENTER_X
33+ y += CANVAS_CENTER_Y
34+
35+ return int (x ), int (y )
36+
37+ """
38+ 随机内部扩散
39+ -x: 原x
40+ -y: 原y
41+ -beta: 强度
42+ """
43+ def scatter_inside (x , y , beta = 0.15 ):
44+ ratio_x = - beta * log (random .random ())
45+ ratio_y = - beta * log (random .random ())
46+
47+ dx = ratio_x * (x - CANVAS_CENTER_X )
48+ dy = ratio_y * (y - CANVAS_CENTER_Y )
49+
50+ return x - dx , y - dy
51+
52+
53+ """
54+ 抖动
55+ -x: 原x
56+ -y: 原y
57+ -ratio: 比例
58+ """
59+ def shrink (x , y , ratio ):
60+
61+ force = - 1 / (((x - CANVAS_CENTER_X ) ** 2 + (y - CANVAS_CENTER_Y ) ** 2 ) ** 0.6 ) # 这个参数...
62+ dx = ratio * force * (x - CANVAS_CENTER_X )
63+ dy = ratio * force * (y - CANVAS_CENTER_Y )
64+ return x - dx , y - dy
65+
66+
67+ """
68+ 自定义曲线函数,调整跳动周期
69+ -p: 参数
70+ """
71+ def curve (p ):
72+ # 可以尝试换其他的动态函数,达到更有力量的效果(贝塞尔?)
73+ return 2 * (2 * sin (4 * p )) / (2 * pi )
74+
75+
76+ # 爱心类
77+ class Heart :
78+ def __init__ (self , generate_frame = 20 ):
79+ # 原始爱心坐标集合
80+ self ._points = set ()
81+ # 边缘扩散效果点坐标集合
82+ self ._edge_diffusion_points = set ()
83+ # 中心扩散效果点坐标集合
84+ self ._center_diffusion_points = set ()
85+ # 每帧动态点坐标
86+ self .all_points = {}
87+ self .build (2000 )
88+
89+ self .random_halo = 1000
90+
91+ self .generate_frame = generate_frame
92+ for frame in range (generate_frame ):
93+ self .calc (frame )
94+
95+ def build (self , number ):
96+ # 爱心
97+ for _ in range (number ):
98+ # 随机不到的地方造成爱心有缺口
99+ t = random .uniform (0 , 2 * pi )
100+ x , y = heart_function (t )
101+ self ._points .add ((x , y ))
102+
103+ # 爱心内扩散
104+ for _x , _y in list (self ._points ):
105+ for _ in range (3 ):
106+ x , y = scatter_inside (_x , _y , 0.05 )
107+ self ._edge_diffusion_points .add ((x , y ))
108+
109+ # 爱心内再次扩散
110+ point_list = list (self ._points )
111+ for _ in range (10000 ):
112+ x , y = random .choice (point_list )
113+ x , y = scatter_inside (x , y , 0.27 )
114+ self ._center_diffusion_points .add ((x , y ))
115+
116+ @staticmethod
117+ def calc_position (x , y , ratio ):
118+ # 调整缩放比例
119+ force = 1 / (((x - CANVAS_CENTER_X ) ** 2 + (y - CANVAS_CENTER_Y ) ** 2 ) ** 0.420 ) # 魔法参数
120+
121+ dx = ratio * force * (x - CANVAS_CENTER_X ) + random .randint (- 1 , 1 )
122+ dy = ratio * force * (y - CANVAS_CENTER_Y ) + random .randint (- 1 , 1 )
123+
124+ return x - dx , y - dy
125+
126+ def calc (self , generate_frame ):
127+ # 圆滑的周期的缩放比例
128+ ratio = 15 * curve (generate_frame / 10 * pi )
129+
130+ halo_radius = int (4 + 6 * (1 + curve (generate_frame / 10 * pi )))
131+ halo_number = int (3000 + 4000 * abs (curve (generate_frame / 10 * pi ) ** 2 ))
132+
133+ all_points = []
134+
135+ # 光环
136+ heart_halo_point = set ()
137+ # 光环的点坐标集合
138+ for _ in range (halo_number ):
139+ # 随机不到的地方造成爱心有缺口
140+ t = random .uniform (0 , 2 * pi )
141+ # 魔法参数
142+ x , y = heart_function (t , shrink_ratio = - 15 )
143+ x , y = shrink (x , y , halo_radius )
144+ if (x , y ) not in heart_halo_point :
145+ # 处理新的点
146+ heart_halo_point .add ((x , y ))
147+ x += random .randint (- 60 , 60 )
148+ y += random .randint (- 60 , 60 )
149+ size = random .choice ((1 , 1 , 2 ))
150+ all_points .append ((x , y , size ))
151+ all_points .append ((x + 20 , y + 20 , size ))
152+ all_points .append ((x - 20 , y - 20 , size ))
153+ all_points .append ((x + 20 , y - 20 , size ))
154+ all_points .append ((x - 20 , y + 20 , size ))
155+
156+ # 轮廓
157+ for x , y in self ._points :
158+ x , y = self .calc_position (x , y , ratio )
159+ size = random .randint (1 , 3 )
160+ all_points .append ((x , y , size ))
161+
162+ # 内容
163+ for x , y in self ._edge_diffusion_points :
164+ x , y = self .calc_position (x , y , ratio )
165+ size = random .randint (1 , 2 )
166+ all_points .append ((x , y , size ))
167+
168+ for x , y in self ._center_diffusion_points :
169+ x , y = self .calc_position (x , y , ratio )
170+ size = random .randint (1 , 2 )
171+ all_points .append ((x , y , size ))
172+
173+ self .all_points [generate_frame ] = all_points
174+
175+ def render (self , render_canvas , render_frame ):
176+ for x , y , size in self .all_points [render_frame % self .generate_frame ]:
177+ render_canvas .create_rectangle (x , y , x + size , y + size , width = 0 , fill = HEART_COLOR )
178+
179+
180+ def draw (main : Tk , render_canvas : Canvas , render_heart : Heart , render_frame = 0 ):
181+ render_canvas .delete ('all' )
182+ render_heart .render (render_canvas , render_frame )
183+ main .after (1 , draw , main , render_canvas , render_heart , render_frame + 1 )
184+
185+
186+ if __name__ == '__main__' :
187+ root = Tk ()
188+ canvas = Canvas (root , bg = 'black' , height = CANVAS_HEIGHT , width = CANVAS_WIDTH )
189+ canvas .pack ()
190+ heart = Heart ()
191+ draw (root , canvas , heart )
192+ root .mainloop ()
0 commit comments