Python 浪漫编程:从零构建一颗“跳动”的粒子爱心


头像
派森小新
原创
发布时间: 2026-10-08 10:25:23 | 阅读数 0收藏数 0评论数 0
封面
本文将使用 Python 代码绘制一颗专属于程序员的浪漫爱心,让代码不再只是逻辑,而成为表达情感的另一种方式。
1

画出爱心骨架

接下来使用Tkinter绘制一个心形曲线,主要使用到的参数方程:

x(t)=16sin3(t)

y(t)=−(13cost−5cos2t−2cos3t−cos4t)​

import random
from math import sin, cos, pi
from tkinter import *

# 1. 基础配置
CANVAS_WIDTH = 640 # 画布宽度
CANVAS_HEIGHT = 480 # 画布高度
CANVAS_CENTER_X = CANVAS_WIDTH / 2 # 中心点X坐标
CANVAS_CENTER_Y = CANVAS_HEIGHT / 2 # 中心点Y坐标
IMAGE_ENLARGE = 11 # 放大倍数
HEART_COLOR = "#FFC0CB" # 粉色

# 2. 核心:心形参数方程
def heart_function(t, shrink_ratio=IMAGE_ENLARGE):
"""
经典心形曲线方程
t: 角度参数 (0 ~ 2π)
"""
x = 16 * (sin(t) ** 3)
y = -(13 * cos(t) - 5 * cos(2*t) - 2 * cos(3*t) - cos(4*t))
# 放大并居中
x *= shrink_ratio
y *= shrink_ratio
x += CANVAS_CENTER_X
y += CANVAS_CENTER_Y
return int(x), int(y)

# 3. 创建窗口并绘制
if __name__ == '__main__':
root = Tk()
canvas = Canvas(root, bg='black', height=CANVAS_HEIGHT, width=CANVAS_WIDTH)
canvas.pack()

# 生成2000个轮廓点
for _ in range(2000):
t = random.uniform(0, 2 * pi)
x, y = heart_function(t)
# 用一个小矩形模拟像素点
size = 2
canvas.create_rectangle(x, y, x+size, y+size, width=0, fill=HEART_COLOR)

root.mainloop()
2

粒子扩散

在心形渲染优化中,引入对数散射算法替代了原有的纯轮廓绘制,成功将心形从线性描边转化为具有实体感的均匀光斑。

import random
from math import sin, cos, pi
from tkinter import *
from math import log # 新增导入

# 1. 基础配置
CANVAS_WIDTH = 640
CANVAS_HEIGHT = 480
CANVAS_CENTER_X = CANVAS_WIDTH / 2
CANVAS_CENTER_Y = CANVAS_HEIGHT / 2
IMAGE_ENLARGE = 11 # 放大倍数
HEART_COLOR = "#FFC0CB" # 粉色

# 新增:内部散射算法
def scatter_inside(x, y, beta=0.15):
"""
利用对数分布让点向中心收缩
beta越大,点越靠近中心
"""
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

# 2. 核心:心形参数方程
def heart_function(t, shrink_ratio=IMAGE_ENLARGE):
"""
经典心形曲线方程
t: 角度参数 (0 ~ 2π)
"""
x = 16 * (sin(t) ** 3)
y = -(13 * cos(t) - 5 * cos(2 * t) - 2 * cos(3 * t) - cos(4 * t))

# 放大并居中
x *= shrink_ratio
y *= shrink_ratio
x += CANVAS_CENTER_X
y += CANVAS_CENTER_Y
return int(x), int(y)



# 3. 创建窗口并绘制
if __name__ == '__main__':
root = Tk()
canvas = Canvas(root, bg='black', height=CANVAS_HEIGHT, width=CANVAS_WIDTH)
canvas.pack()

# 生成2000个轮廓点
for _ in range(2000):
t = random.uniform(0, 2 * pi)
x, y = heart_function(t)
# 用一个小矩形模拟像素点
size = 2
canvas.create_rectangle(x, y, x + size, y + size, width=0, fill=HEART_COLOR)
points = set()
# 1. 原始轮廓点
for _ in range(2000):
t = random.uniform(0, 2 * pi)
x, y = heart_function(t)
points.add((x, y))

# 2. 边缘扩散点(少量,紧贴轮廓内侧)
edge_points = set()
for px, py in list(points):
for _ in range(3):
x, y = scatter_inside(px, py, 0.05) # beta小,扩散范围小
edge_points.add((x, y))

# 3. 中心填充点(大量,营造朦胧感)
center_points = set()
point_list = list(points)
for _ in range(4000):
px, py = random.choice(point_list)
x, y = scatter_inside(px, py, 0.17) # beta大,更靠中心
center_points.add((x, y))

# 分别绘制三种点(可用不同大小区分层次)
for x, y in points:
canvas.create_rectangle(x, y, x + 2, y + 2, width=0, fill=HEART_COLOR)
for x, y in edge_points:
canvas.create_rectangle(x, y, x + 1, y + 1, width=0, fill=HEART_COLOR)
for x, y in center_points:
canvas.create_rectangle(x, y, x + 1, y + 1, width=0, fill=HEART_COLOR)

root.mainloop()
3

“心”跳动

心形动态化:由静转动,添加心跳循环动画。

import random
from math import sin, cos, pi ,log
from tkinter import *

# 1. 基础配置
CANVAS_WIDTH = 640
CANVAS_HEIGHT = 480
CANVAS_CENTER_X = CANVAS_WIDTH / 2
CANVAS_CENTER_Y = CANVAS_HEIGHT / 2
IMAGE_ENLARGE = 11 # 放大倍数
HEART_COLOR = "#FFC0CB" # 粉色

# 新增:内部散射算法
def scatter_inside(x, y, beta=0.15):
"""
利用对数分布让点向中心收缩
beta越大,点越靠近中心
"""
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

# 2. 核心:心形参数方程
def heart_function(t, shrink_ratio=IMAGE_ENLARGE):
"""
经典心形曲线方程
t: 角度参数 (0 ~ 2π)
"""
x = 16 * (sin(t) ** 3)
y = -(13 * cos(t) - 5 * cos(2 * t) - 2 * cos(3 * t) - cos(4 * t))

# 放大并居中
x *= shrink_ratio
y *= shrink_ratio
x += CANVAS_CENTER_X
y += CANVAS_CENTER_Y
return int(x), int(y)


# 新增:平滑周期曲线
def curve(p):
"""返回 [-1, 1] 之间的平滑振荡值"""
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.generate_frame = generate_frame

self.build(2000) # 构建基础点集(同第二步)
for frame in range(generate_frame):
self.calc(frame) # 预计算所有帧

def build(self, number):
"""
构建爱心的三层粒子点集
:param number: 原始轮廓点的数量
"""
# 1. 生成原始爱心轮廓点
for _ in range(number):
t = random.uniform(0, 2 * pi)
x, y = heart_function(t)
self._points.add((x, y))

# 2. 生成边缘扩散点(紧贴轮廓内侧,模拟边缘柔化)
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))

# 3. 生成中心扩散点(大量填充内部,营造朦胧实心感)
point_list = list(self._points)
for _ in range(4000):
x, y = random.choice(point_list)
x, y = scatter_inside(x, y, 0.17)
self._center_diffusion_points.add((x, y))

@staticmethod
def calc_position(x, y, ratio):
"""根据ratio对每个点做径向缩放"""
force = 1 / (((x - CANVAS_CENTER_X) ** 2 + (y - CANVAS_CENTER_Y) ** 2) ** 0.520)
dx = ratio * force * (x - CANVAS_CENTER_X)
dy = ratio * force * (y - CANVAS_CENTER_Y)
return x - dx, y - dy

def calc(self, frame):
ratio = 10 * curve(frame / 10 * pi) # 当前帧的缩放比
all_pts = []
for x, y in self._points | self._edge_diffusion_points | self._center_diffusion_points:
nx, ny = self.calc_position(x, y, ratio)
size = random.randint(1, 3)
all_pts.append((nx, ny, size))
self.all_points[frame] = all_pts

def render(self, canvas, frame):
for x, y, size in self.all_points[frame % self.generate_frame]:
canvas.create_rectangle(x, y, x + size, y + size, width=0, fill=HEART_COLOR)


# 动画驱动
def draw(main, canvas, heart, frame=0):
canvas.delete('all')
heart.render(canvas, frame)
main.after(160, draw, main, canvas, heart, frame + 1) # 160ms/帧 ≈ 6FPS


# 3. 创建窗口并绘制
if __name__ == '__main__':
root = Tk()
canvas = Canvas(root, bg='black', height=CANVAS_HEIGHT, width=CANVAS_WIDTH)
canvas.pack()
heart = Heart()
draw(root, canvas, heart)
root.mainloop()
4

光子特效

这是最大的升级。光晕不再是静态的,而是与心跳节拍同步呼吸:

import random
from math import sin, cos, pi, log
from tkinter import *

# 1. 基础配置
CANVAS_WIDTH = 640
CANVAS_HEIGHT = 480
CANVAS_CENTER_X = CANVAS_WIDTH / 2
CANVAS_CENTER_Y = CANVAS_HEIGHT / 2
IMAGE_ENLARGE = 11 # 放大倍数
HEART_COLOR = "#FFC0CB" # 粉色

# 新增:内部散射算法
def scatter_inside(x, y, beta=0.15):
"""
利用对数分布让点向中心收缩
beta越大,点越靠近中心
"""
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

# 2. 核心:心形参数方程
def heart_function(t, shrink_ratio=IMAGE_ENLARGE):
"""
经典心形曲线方程
t: 角度参数 (0 ~ 2π)
"""
x = 16 * (sin(t) ** 3)
y = -(13 * cos(t) - 5 * cos(2 * t) - 2 * cos(3 * t) - cos(4 * t))

# 放大并居中
x *= shrink_ratio
y *= shrink_ratio
x += CANVAS_CENTER_X
y += CANVAS_CENTER_Y
return int(x), int(y)


# 新增:平滑周期曲线
def curve(p):
"""返回 [-1, 1] 之间的平滑振荡值"""
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.generate_frame = generate_frame

self.build(2000) # 构建基础点集(同第二步)
for frame in range(generate_frame):
self.calc(frame) # 预计算所有帧

def build(self, number):
"""
构建爱心的三层粒子点集
:param number: 原始轮廓点的数量
"""
# 1. 生成原始爱心轮廓点
for _ in range(number):
t = random.uniform(0, 2 * pi)
x, y = heart_function(t)
self._points.add((x, y))

# 2. 生成边缘扩散点(紧贴轮廓内侧,模拟边缘柔化)
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))

# 3. 生成中心扩散点(大量填充内部,营造朦胧实心感)
point_list = list(self._points)
for _ in range(4000):
x, y = random.choice(point_list)
x, y = scatter_inside(x, y, 0.17)
self._center_diffusion_points.add((x, y))

@staticmethod
def calc_position(x, y, ratio):
"""根据ratio对每个点做径向缩放"""
force = 1 / (((x - CANVAS_CENTER_X) ** 2 + (y - CANVAS_CENTER_Y) ** 2) ** 0.520)
dx = ratio * force * (x - CANVAS_CENTER_X)
dy = ratio * force * (y - CANVAS_CENTER_Y)
return x - dx, y - dy

def calc(self, generate_frame):
# 1. 计算当前帧的缩放比和光晕参数
ratio = 10 * 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 作为局部变量名,避免 NameError
all_points = []
heart_halo_point = set()

# 2. 计算光晕粒子
for _ in range(halo_number):
t = random.uniform(0, 2 * pi)
x, y = heart_function(t, shrink_ratio=11.6)
x, y = shrink(x, y, halo_radius)
if (x, y) not in heart_halo_point:
heart_halo_point.add((x, y))
x += random.randint(-14, 14)
y += random.randint(-14, 14)
size = random.choice((1, 2, 2))
all_points.append((x, y, size))

# 轮廓点:稍大 (1~3),保证边缘清晰
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))

# 边缘扩散点:较小 (1~2),柔化边缘
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))

# 中心扩散点:较小 (1~2),营造内部朦胧感
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))

# 3. 将当前帧的所有点存入字典
self.all_points[generate_frame] = all_points

def render(self, canvas, frame):
for x, y, size in self.all_points[frame % self.generate_frame]:
canvas.create_rectangle(x, y, x + size, y + size, width=0, fill=HEART_COLOR)
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

# 动画驱动
def draw(main, canvas, heart, frame=0):
canvas.delete('all')
heart.render(canvas, frame)
main.after(160, draw, main, canvas, heart, frame + 1) # 160ms/帧 ≈ 6FPS


# 3. 创建窗口并绘制
if __name__ == '__main__':
root = Tk()
canvas = Canvas(root, bg='black', height=CANVAS_HEIGHT, width=CANVAS_WIDTH)
canvas.pack()
heart = Heart()
draw(root, canvas, heart)
root.mainloop()
阅读记录0
点赞0
收藏0
禁止 本文未经作者允许授权,禁止转载
猜你喜欢
评论/提问(已发布 0 条)
头像
评论 评论
收藏 收藏
分享 分享
pdf下载 下载
pdf下载 举报