正文
01-效果演示
下图是代码运行之后的爱心显示结果:
下面的视频该爱心是动态效果,较为简洁,如果需要使用,可以进行完善,这里只是一个趣味实战,下面将对代码实现进行非常详细地描述:
浪漫爱心
02-代码实现
下图是动态爱心代码实现,对所有代码进行详细解释如下:
import random // 导入Python中的random模块,用于生成随机数。
from math import sin, cos, pi, log // 从math模块中导入sin、cos、pi和log函数,用于数学计算。
from tkinter import * // 从tkinter模块中导入所有内容,用于创建GUI界面。
CANVAS_WIDTH = 640
CANVAS_HEIGHT = 480
CANVAS_CENTER_X = CANVAS_WIDTH / 2
CANVAS_CENTER_Y = CANVAS_HEIGHT / 2
// 定义了一些常量:CANVAS_WIDTH, CANVAS_HEIGHT, CANVAS_CENTER_X, CANVAS_CENTER_Y,用于定义画布的大小和中心位置。
def center_window(root, width, height): // 定义了一个函数center_window,接受三个参数root(窗口对象),width(窗口宽度),height(窗口高度)
screenwidth = root.winfo_screenwidth(): // 使用winfo_screenwidth()方法获取屏幕的宽度,并将值赋给screenwidth变量
screenheight = root.winfo_screenheight(): // 使用winfo_screenheight()方法获取屏幕的高度,并将值赋给screenheight变量
size = '%dx%d+%d+%d' % (width, height, (screenwidth - width) / 2, (screenheight - height) / 2): // 构建一个字符串size,其中包括窗口的宽度和高度,以及窗口左上角的位置(居中显示计算)
root.geometry(size): // 使用geometry方法将窗口的大小和位置设置为size字符串中描述的值,从而将窗口居中显示在屏幕上
def heart_function(t, shrink_ratio): // 定义了一个心形函数heart_function,接受两个参数t(参数值)和shrink_ratio(缩小比率)
x = 16 * (sin(t) ** 3): // 计算心形曲线上点的x坐标,根据参数t的正弦值的三次方乘以16来计算
y = -(13 * cos(t) - 5 * cos(2 * t) - 2 * cos(3 * t) - cos(4 * t)): // 计算心形曲线上点的y坐标,根据参数t的余弦值的线性组合来计算
x *= shrink_ratio: // 将计算出的x坐标乘以缩小比率shrink_ratio,用于对心形曲线进行缩放
y *= shrink_ratio: // 将计算出的y坐标乘以缩小比率shrink_ratio,用于对心形曲线进行缩放
x += CANVAS_CENTER_X: // 将x坐标移动到画布横向中心位置
y += CANVAS_CENTER_Y: // 将y坐标移动到画布纵向中心位置
return int(x), int(y): // 返回计算出的x和y坐标,使用int()函数将结果转换为整数类型。这样就得到了在画布上绘制心形图案所需要的坐标
Heart类用于生成心形图案,初始化时会生成心形曲线上的点,并根据一定的规则进行扩散。生成心形图案后,可以根据指定的帧数进行渲染。下面逐个分析:
def __init__(self, generate_frame=20): // 定义了Heart类的初始化方法,接受一个参数generate_frame,默认值为20。在这个方法中进行了一些初始化操作
self._points = set(): // 创建一个空集合_points,用于存储心形图案的点
self._edge_diffusion_points = set(): // 创建一个空集合_edge_diffusion_points,用于存储心形图案的边缘扩散点
self._center_diffusion_points = set(): // 创建一个空集合_center_diffusion_points,用于存储心形图案的中心扩散点
self.all_points = {}: // 创建一个空字典all_points,用于存储所有计算出的点
self.build(2000): // 调用build方法,传入参数2000,用于构建心形图案
self.random_halo = 1000: // 设置random_halo属性为1000,用于表示随机光晕的大小
self.generate_frame = generate_frame: // 将传入的generate_frame参数赋值给实例属性generate_frame,表示生成帧数
for frame in range(generate_frame):: // 遍历生成帧数的范围
self.calc(frame): // 调用calc方法,传入当前帧数frame作为参数,用于计算心形图案的点的位置。通过循环生成帧数次来完整绘制心形图案
def build(self, number): // 定义了Heart类中的build方法,接受一个参数number,用于生成指定数量的点
for _ in range(number):: // 遍历指定数量的次数
t = random.uniform(0, 2 * pi): // 生成一个随机的角度t,范围在0到2π之间
x, y = heart_function(t, 11): // 调用之前定义的heart_function函数,传入角度t和缩小比率11,计算得到心形曲线上的点的x和y坐标
self._points.add((x, y)): // 将计算出的点的坐标添加到_points集合中,第一个for循环结束后,_points集合中包含了心形曲线上的点
for _x, _y in list(self._points):: // 遍历_points集合中的所有点
for _ in range(3):: // 嵌套循环,执行3次
self._edge_diffusion_points.add((x, y)): // 将当前点加入_edge_diffusion_points集合中,实现边缘扩散
point_list = list(self._points): // 将_points集合转换为列表point_list
for _ in range(4000):: // 遍历4000次
x, y = random.choice(point_list): // 从point_list中随机选择一个点的坐标
self._center_diffusion_points.add((x, y)): // 将选定的点作为中心点,加入_center_diffusion_points集合中,实现中心扩散
@staticmethod: // 标记下面的calc_position方法为静态方法,即不需要实例化对象也可以直接调用该方法
def calc_position(x, y, ratio): // 定义了静态方法calc_position,接受三个参数x(x坐标)、y(y坐标)和ratio(比率)
force = 1 / (((x - CANVAS_CENTER_X) ** 2 + (y - CANVAS_CENTER_Y) ** 2) ** 0.520): // 计算力(force),根据点到画布中心的距离的0.520次方来计算
dx = ratio * force * (x - CANVAS_CENTER_X) + random.randint(-1, 1): // 计算x方向上的偏移量,根据比率、力和距离画布中心的偏移,再加上一个随机值
dy = ratio * force * (y - CANVAS_CENTER_Y) + random.randint(-1, 1): // 计算y方向上的偏移量,同样根据比率、力和距离画布中心的偏移,再加上一个随机值
return x - dx, y - dy: // 返回新计算出的x和y坐标,通过减去对应的偏移量,实现点的位置更新
这个静态方法calc_position用于根据点与画布中心的距离,以及指定的比率,计算点的新位置偏移量,从而实现点的移动。同时添加了一些随机性以增加点的变化
def calc(self, generate_frame): // 定义了Heart类中的calc方法,接受一个参数generate_frame,用于计算心形图案的点的位置
ratio = 10: // 设置比率为10
halo_radius = int(4 + 6): // 计算光晕的半径为10
halo_number = int(3000 + 4000): // 设置生成的光晕点的数量在7000左右
all_points = []: // 创建一个空列表all_points,用于存储所有计算出的点
heart_halo_point = set(): // 创建一个空集合heart_halo_point,用于存储心形光晕点
第一个循环用于生成心形光晕点,并添加到all_points列表中。随机选取角度t,计算心形曲线上的点坐标,加入heart_halo_point集合,加入随机偏移,选择大小,将点信息加入all_points列表
第二个循环遍历心形曲线上的点,通过调用calc_position方法计算新的位置坐标,加入all_points列表
第三个循环遍历边缘扩散点,同样通过调用calc_position方法计算新的位置坐标,加入all_points列表
self.all_points[generate_frame] = all_points: // 将生成的所有点信息存储在all_points字典中,以当前帧数generate_frame作为键值
通过这些步骤,calc方法计算了心形图案上的点的新的位置,并将结果存储在all_points字典中
def render(self, render_canvas, render_frame): // 定义了Heart类中的render方法,接受两个参数render_canvas(用于绘制点的画布)和render_frame(当前帧数)
for x, y, size in self.all_points[render_frame % self.generate_frame]:: // 遍历all_points字典中与当前帧数对应的点的信息
alpha = int(((render_frame % self.generate_frame) / self.generate_frame) * 255): // 计算当前帧数对总帧数取余再除以总帧数后的比例,并将其转换为alpha值(0-255范围)
color = f"#{alpha:02X}0000": // 根据计算得到的alpha值生成颜色字符串,格式为RGBA中的R=alpha,G=0,B=0,表示红色
render_canvas.create_rectangle(x, y, x + size, y + size, width=0, fill=color): // 在绘制点的画布上创建一个矩形,以(x, y)为左上角坐标,(x+size, y+size)为右下角坐标,填充颜色为之前计算得到的红色
root = Tk(): // 创建一个Tkinter应用程序的根窗口对象root
root.title("浪漫爱心"): // 设置窗口的标题为"浪漫爱心"
center_window(root, CANVAS_WIDTH, CANVAS_HEIGHT): // 调用center_window函数来将窗口居中显示在屏幕上,参数包括窗口对象root以及指定的画布宽度和高度
canvas = Canvas(root, bg='black', height=CANVAS_HEIGHT, width=CANVAS_WIDTH): // 在窗口root上创建一个Canvas对象canvas,背景颜色为黑色,指定画布的高度和宽度为预定义的值CANVAS_HEIGHT和CANVAS_WIDTH
canvas.pack(): // 将Canvas对象canvas放置到窗口中进行显示
heart = Heart(): // 创建一个Heart类的实例对象heart,用于生成心形图案并进行后续的计算和渲染
通过以上代码,创建了一个Tkinter窗口并在窗口中显示了一个黑色背景的Canvas画布,同时生成了一个Heart类的实例对象heart,准备后续使用该对象进行心形图案的绘制和动画效果展示。
def draw(main, render_canvas, render_heart, render_frame=0): // 定义了一个名为draw的函数,接受四个参数,分别为main(主窗口对象),render_canvas(用于绘制的画布对象),render_heart(Heart类实例对象),render_frame(当前帧数,默认为0)
render_canvas.delete('all'): // 在每次绘制之前,清空画布上的所有内容
render_heart.render(render_canvas, render_frame): // 调用Heart类中的render方法,将当前帧数和绘制的画布传入,用于绘制心形图案上的点
main.after(300, draw, main, render_canvas, render_heart, render_frame + 1): // 使用Tkinter中的after方法,设置300毫秒后执行draw函数,实现动画效果。同时将main窗口对象、render_canvas画布对象、render_heart对象以及更新后的render_frame作为参数传入
Label(root, text="I Love You", bg="black", fg="#FF0000", font="Helvetic 20 bold").place(relx=.5, rely=.5, anchor=CENTER): // 创建一个标签对象,显示文本"I Love You",设置背景颜色为黑色,前景色为红色,字体为Helvetica 20粗体,在窗口中居中显示
root.mainloop(): // 进入Tkinter的主事件循环,等待响应用户事件,保持窗口显示
通过以上代码,实现了一个动画效果,不断更新心形图案的点的位置并在画布上绘制,同时在窗口中显示 “I Love You” 的标签文本。整个程序会持续运行,直到用户关闭窗口。
import random
from math import sin, cos, pi, log
from tkinter import *CANVAS_WIDTH = 640
CANVAS_HEIGHT = 480
CANVAS_CENTER_X = CANVAS_WIDTH / 2
CANVAS_CENTER_Y = CANVAS_HEIGHT / 2def center_window(root, width, height):screenwidth = root.winfo_screenwidth() screenheight = root.winfo_screenheight() size = '%dx%d+%d+%d' % (width, height, (screenwidth - width) / 2, (screenheight - height) / 2) root.geometry(size) def heart_function(t, shrink_ratio):x = 16 * (sin(t) ** 3)y = -(13 * cos(t) - 5 * cos(2 * t) - 2 * cos(3 * t) - cos(4 * t))x *= shrink_ratioy *= shrink_ratiox += CANVAS_CENTER_Xy += CANVAS_CENTER_Yreturn int(x), int(y)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 = 1000self.generate_frame = generate_framefor 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, 11)self._points.add((x, y))for _x, _y in list(self._points):for _ in range(3):x, y = _x, _yself._edge_diffusion_points.add((x, y))point_list = list(self._points)for _ in range(4000):x, y = random.choice(point_list)self._center_diffusion_points.add((x, y))@staticmethoddef calc_position(x, y, ratio):force = 1 / (((x - CANVAS_CENTER_X) ** 2 + (y - CANVAS_CENTER_Y) ** 2) ** 0.520)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 - dydef calc(self, generate_frame):ratio = 10halo_radius = int(4 + 6)halo_number = int(3000 + 4000)all_points = []heart_halo_point = set()for _ in range(halo_number):t = random.uniform(0, 2 * pi)x, y = heart_function(t, 11.6)x, y = x, yif (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))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))self.all_points[generate_frame] = all_pointsdef render(self, render_canvas, render_frame):for x, y, size in self.all_points[render_frame % self.generate_frame]:alpha = int(((render_frame % self.generate_frame) / self.generate_frame) * 255)color = f"#{alpha:02X}0000"render_canvas.create_rectangle(x, y, x + size, y + size, width=0, fill=color)root = Tk()
root.title("浪漫爱心")
center_window(root, CANVAS_WIDTH, CANVAS_HEIGHT) canvas = Canvas(root, bg='black', height=CANVAS_HEIGHT, width=CANVAS_WIDTH)
canvas.pack()heart = Heart()def draw(main, render_canvas, render_heart, render_frame=0):render_canvas.delete('all')render_heart.render(render_canvas, render_frame)main.after(300, draw, main, render_canvas, render_heart, render_frame + 1)draw(root, canvas, heart, 0)Label(root, text="I Love You", bg="black", fg="#FF0000", font="Helvetic 20 bold").place(relx=.5, rely=.5, anchor=CENTER)root.mainloop()