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