好家伙,令女神尖叫的李峋同款爱心代码来了!

最近有一部虐中带糖的剧,热度直线上升中,这部剧就是《点燃我,温暖你》。陈飞宇饰演的李峋是一个问题少年,但是同时也是一个编程天才,李峋在课程考试中提交的爱心代码着实是戳了一波粉丝的心!

这也太会了!爱心代码还因此浅浅地上了一波热搜。

我不爱刷剧,特别是这种流量明星的剧,所以没看过这电视剧。但奈何不住小女生看啊,看了之后还过来说,你是程序员,你会吗?

这一下就把我问懵了,这种玩意咱们一般也不会去研究,工作上基本也用不着。

而且这种效果用 HTML 前端网页实现比较容易,效果也会比较好!

硬着头皮去问了一下度娘,好家伙,还真有一些人做了出来,真是羡慕这些人,咱们这种加班狗心有余而力不足啊!

但是在他们实现的基础上略微加工加工,变些花样还是可以的。

实现

其实,画这个爱心,主要有几个要素:

  • 基础爱心
  • 爱心周边的散列点
  • 爱心抖动

这几个要素,都有对应的数学算法,套用这些算法就可以实现。

部分代码如下(完整代码文末获取):

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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
"""
    爱心函数生成器
    -shrink_ratio: 放大比例
    -t: 参数
"""
def heart_function(t, shrink_ratio: float = IMAGE_ENLARGE):
    # 基础函数
    x = 17 * (sin(t) ** 3)
    y = -(16 * cos(t) - 5 * cos(2 * t) - 2 * cos(3 * t) - cos(3 * t))
 
    # 放大
    x*=IMAGE_ENLARGE
    y*=IMAGE_ENLARGE
    # 移到画布中央
    x += CANVAS_CENTER_X
    y += CANVAS_CENTER_Y
 
    return int(x), int(y)
 
"""
    随机内部扩散
    -x: 原x
    -y: 原y
    -beta: 强度
""" 
def scatter_inside(x, y, beta=0.15):
    ratio_x = - beta * log(random.random())
    ratio_y = - beta * log(random.random())
 
    dx = ratio_x * (x - CANVAS_CENTER_X)
    dy = ratio_y * (y - CANVAS_CENTER_Y)
 
    return x - dx, y - dy
 
 
"""
    抖动
    -x: 原x
    -y: 原y
    -ratio: 比例
""" 
def shrink(x, y, ratio):
   
    force = -1 / (((x - CANVAS_CENTER_X) ** 2 + (y - CANVAS_CENTER_Y) ** 2) ** 0.6)  # 这个参数...
    dx = ratio * force * (x - CANVAS_CENTER_X)
    dy = ratio * force * (y - CANVAS_CENTER_Y)
    return x - dx, y - dy
 
 
"""
    自定义曲线函数,调整跳动周期
    -p: 参数
"""
def curve(p):
    # 可以尝试换其他的动态函数,达到更有力量的效果(贝塞尔?)
    return 2 * (2 * sin(4 * p)) / (2 * pi)
 

# 爱心类
class Heart:
    def __init__(self, generate_frame=20):
        # 原始爱心坐标集合
        self._points = set()  
        # 边缘扩散效果点坐标集合
        self._edge_diffusion_points = set()  
        # 中心扩散效果点坐标集合
        self._center_diffusion_points = set()  
        # 每帧动态点坐标
        self.all_points = {}  
        self.build(2000)
 
        self.random_halo = 1000
 
        self.generate_frame = generate_frame
        for frame in range(generate_frame):
            self.calc(frame)
 
    def build(self, number):
        # 爱心
        for _ in range(number):
            # 随机不到的地方造成爱心有缺口
            t = random.uniform(0, 2 * pi)  
            x, y = heart_function(t)
            self._points.add((x, y))
 
        # 爱心内扩散
        for _x, _y in list(self._points):
            for _ in range(3):
                x, y = scatter_inside(_x, _y, 0.05)
                self._edge_diffusion_points.add((x, y))
 
        # 爱心内再次扩散
        point_list = list(self._points)
        for _ in range(10000):
            x, y = random.choice(point_list)
            x, y = scatter_inside(x, y, 0.27)
            self._center_diffusion_points.add((x, y))
 
    @staticmethod
    def calc_position(x, y, ratio):
        # 调整缩放比例
        force = 1 / (((x - CANVAS_CENTER_X) ** 2 + (y - CANVAS_CENTER_Y) ** 2) ** 0.420)  # 魔法参数
 
        dx = ratio * force * (x - CANVAS_CENTER_X) + random.randint(-1, 1)
        dy = ratio * force * (y - CANVAS_CENTER_Y) + random.randint(-1, 1)
 
        return x - dx, y - dy
 
    def calc(self, generate_frame):
         # 圆滑的周期的缩放比例
        ratio = 15 * curve(generate_frame / 10 * pi) 
 
        halo_radius = int(4 + 6 * (1 + curve(generate_frame / 10 * pi)))
        halo_number = int(3000 + 4000 * abs(curve(generate_frame / 10 * pi) ** 2))
 
        all_points = []
 
        # 光环
        heart_halo_point = set()  
        # 光环的点坐标集合
        for _ in range(halo_number):
            # 随机不到的地方造成爱心有缺口
            t = random.uniform(0, 2 * pi)  
            # 魔法参数
            x, y = heart_function(t, shrink_ratio=-15)  
            x, y = shrink(x, y, halo_radius)
            if (x, y) not in heart_halo_point:
                # 处理新的点
                heart_halo_point.add((x, y))
                x += random.randint(-60, 60)
                y += random.randint(-60, 60)
                size = random.choice((1, 1, 2))
                all_points.append((x, y, size))
                all_points.append((x+20, y+20, size))
                all_points.append((x-20, y -20, size))
                all_points.append((x+20, y - 20, size))
                all_points.append((x - 20, y +20, size))
 
        # 轮廓
        for x, y in self._points:
            x, y = self.calc_position(x, y, ratio)
            size = random.randint(1, 3)
            all_points.append((x, y, size))
 
        # 内容
        for x, y in self._edge_diffusion_points:
            x, y = self.calc_position(x, y, ratio)
            size = random.randint(1, 2)
            all_points.append((x, y, size))
 
        for x, y in self._center_diffusion_points:
            x, y = self.calc_position(x, y, ratio)
            size = random.randint(1, 2)
            all_points.append((x, y, size))
 
        self.all_points[generate_frame] = all_points
 
    def render(self, render_canvas, render_frame):
        for x, y, size in self.all_points[render_frame % self.generate_frame]:
            render_canvas.create_rectangle(x, y, x + size, y + size, width=0, fill=HEART_COLOR)
 
 
def draw(main: Tk, render_canvas: Canvas, render_heart: Heart, render_frame=0):
    render_canvas.delete('all')
    render_heart.render(render_canvas, render_frame)
    main.after(1, draw, main, render_canvas, render_heart, render_frame + 1)

效果如下:

当然,你也可以改变爱心的颜色,只需要改变 HEART_COLOR 参数即可。

或者,我们可以在爱心的中心加文字:

1
2
3
4
5
6
7
8
9
10
root = Tk()
    canvas = Canvas(root, bg='black', height=CANVAS_HEIGHT, width=CANVAS_WIDTH)
    canvas.pack()
    heart = Heart()
    draw(root, canvas, heart)

    text2 = Label(root, text="爱你",font = ("Helvetica", 18), fg = "#c12bec" ,bg = "black") #
    text2.place(x=395, y=350)

    root.mainloop()

运行效果如下:

总结

待我装模作样噼里啪啦敲过一阵子代码之后,把这个效果展示给那些小MM看时,一个个在那尖呼:太好看了!快教我学 Python 吧!

我心想:你们学啥子 Python ?找个会 Python 的程序员它不香吗?

嘴里却一个劲的说着:好啊!我来给你们开课吧!

Python Geek Tech wechat
欢迎订阅 Python 技术,这里分享关于 Python 的一切。