【python】【绘制小程序】动态爱心绘制

背景介绍

python 代码

python 复制代码
import tkinter as tk
import random
from math import sin, cos, pi, log
from PIL import Image, ImageDraw, ImageFont  # 用于保存图片和生成GIF

width = 888
height = 500
heartx = width / 2
hearty = height / 2
side = 11
heartcolor = "pink"  # 爱心颜色,可修改
# heartcolor = "gold"  # 爱心颜色,可修改

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.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(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):
        force = 1 / (((x - heartx) ** 2 + (y - hearty) ** 2) ** 0.520)  # 魔法参数
        dx = ratio * force * (x - heartx) + random.randint(-1, 1)
        dy = ratio * force * (y - hearty) + random.randint(-1, 1)
        return x - dx, y - dy

    def calc(self, generate_frame):
        ratio = 10 * curve(generate_frame / 10 * pi)  # 圆滑的周期的缩放比例
        all_points = []

        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, draw, render_frame):
        for x, y, size in self.all_points[render_frame % self.generate_frame]:
            draw.rectangle([x, y, x + size, y + size], fill=heartcolor)

def heart_function(t, shrink_ratio: float = side):
    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 += heartx
    y += hearty
    return int(x), int(y)

def scatter_inside(x, y, beta=0.15):
    ratio_x = - beta * log(random.random())
    ratio_y = - beta * log(random.random())
    dx = ratio_x * (x - heartx)
    dy = ratio_y * (y - hearty)
    return x - dx, y - dy

def curve(p):
    return 2 * (2 * sin(4 * p)) / (2 * pi)

# 保存动态爱心为GIF并添加文字
def save_gif():
    frames = []
    heart = Heart()
    for frame in range(heart.generate_frame):
        img = Image.new('RGB', (width, height), 'black')
        draw = ImageDraw.Draw(img)

        # 绘制爱心
        heart.render(draw, frame)

        # 绘制中间的文字
        # text = "I Love You!"
        text = "Miss ❤ U"
        # font = ImageFont.truetype("arial.ttf", 40)  # 确保系统上有 Arial 字体文件或替换为可用的字体文件
        font = ImageFont.truetype("./font/DejaVu-Sans.ttf", 40)  # 确保系统上有 Arial 字体文件或替换为可用的字体文件
        # 获取文本的边界框
        bbox = draw.textbbox((0, 0), text, font=font)
        text_width, text_height = bbox[2] - bbox[0], bbox[3] - bbox[1]
        text_x = (width - text_width) / 2
        text_y = (height - text_height) / 2
        # draw.text((text_x, text_y), text, font=font, fill="#FF99CC")
        draw.text((text_x, text_y), text, font=font, fill="gold")

        frames.append(img)

    # 保存为 GIF 动画
    frames[0].save('./result/1_2_3_heart_animation_with_text.gif', save_all=True, append_images=frames[1:], duration=160, loop=0)

if __name__ == '__main__':
    save_gif()

绘制结果

进阶修改:周围环绕文字,修改背景颜色

python 复制代码
import tkinter as tk
import random
from math import sin, cos, pi, log, atan2
from PIL import Image, ImageDraw, ImageFont

width = 666
height = 460
heartx = width / 2
hearty = height / 2
side = 11
heartcolor = "pink"  # 爱心颜色,可修改
# heartcolor = "gold"  # 爱心颜色,可修改

# 定义马卡龙蓝色
macaron_blue = "#A2C2E1"
cream_white = "#F5F5F5"
cyan_green = "#00FFFF"


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.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(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):
        force = 1 / (((x - heartx) ** 2 + (y - hearty) ** 2) ** 0.520)  # 魔法参数
        dx = ratio * force * (x - heartx) + random.randint(-1, 1)
        dy = ratio * force * (y - hearty) + random.randint(-1, 1)
        return x - dx, y - dy

    def calc(self, generate_frame):
        ratio = 10 * curve(generate_frame / 10 * pi)  # 圆滑的周期的缩放比例
        all_points = []

        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, draw, render_frame):
        for x, y, size in self.all_points[render_frame % self.generate_frame]:
            draw.rectangle([x, y, x + size, y + size], fill=heartcolor)

def heart_function(t, shrink_ratio: float = side):
    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 += heartx
    y += hearty
    return int(x), int(y)

def scatter_inside(x, y, beta=0.15):
    ratio_x = - beta * log(random.random())
    ratio_y = - beta * log(random.random())
    dx = ratio_x * (x - heartx)
    dy = ratio_y * (y - hearty)
    return x - dx, y - dy

def curve(p):
    return 2 * (2 * sin(4 * p)) / (2 * pi)

# 计算最小外接圆的半径
def calculate_bounding_circle_radius():
    max_distance = 0
    for t in range(0, 360):
        radian = t * pi / 180
        x, y = heart_function(radian)
        distance = ((x - heartx) ** 2 + (y - hearty) ** 2) ** 0.5
        if distance > max_distance:
            max_distance = distance
    return max_distance

# 绘制外接圆上的文字
def draw_text_on_circle(draw, text, radius, font):
    # char_angle = 2 * pi / len(text)  # 每个字符的角度
    char_angle = pi / len(text)  # 每个字符的角度
    for i, char in enumerate(text):
        angle = i * char_angle  - pi + 0.19
        angle = angle * -1
        x = heartx + radius * cos(angle)
        y = hearty + radius * sin(angle)
        bbox = draw.textbbox((0, 0), char, font=font)
        text_width, text_height = bbox[2] - bbox[0], bbox[3] - bbox[1]
        # draw.text((x - text_width / 2, y - text_height / 2), char, font=font, fill="pink")
        draw.text((x - text_width / 2, y - text_height / 2), char, font=font, fill=cyan_green)

# 保存动态爱心为GIF并添加文字
def save_gif():
    frames = []
    heart = Heart()
    circle_radius = calculate_bounding_circle_radius() + 20  # 外接圆半径,稍微大一些以避免重叠

    for frame in range(heart.generate_frame):
        # img = Image.new('RGB', (width, height), 'black')
        # img = Image.new('RGB', (width, height), cream_white)       ### 背景颜色修改
        img = Image.new('RGB', (width, height), macaron_blue)       ### 背景颜色修改
        draw = ImageDraw.Draw(img)

        # 绘制爱心
        heart.render(draw, frame)

        # 绘制中间的文字
        text = "爱 你 呦"
        font = ImageFont.truetype("./font/simsun.ttc", 40)
        bbox = draw.textbbox((0, 0), text, font=font)
        text_width, text_height = bbox[2] - bbox[0], bbox[3] - bbox[1]
        text_x = (width - text_width) / 2
        text_y = (height - text_height) / 2
        draw.text((text_x, text_y), text, font=font, fill="gold")
        # draw.text((text_x, text_y), text, font=font, fill="pink")

        # 绘制外接圆上的文字
        # circle_text = "十个勤天 做大做强"
        # circle_font = ImageFont.truetype("./font/simsun.ttc", 36)
        # draw_text_on_circle(draw, circle_text, circle_radius, circle_font)

        frames.append(img)

    # 保存为 GIF 动画
    frames[0].save('./result/1_2_3_heart_animation_with_text.gif', save_all=True, append_images=frames[1:], duration=160, loop=0)

if __name__ == '__main__':
    save_gif()

生成结果

END

相关推荐
YBN娜11 分钟前
设计模式-创建型设计模式
java·开发语言·设计模式
CoderCodingNo13 分钟前
【GESP】C++四级真题 luogu-B4040 [GESP202409 四级] 黑白方块
开发语言·c++
Lynnxiaowen38 分钟前
今天我们学习python编程常用模块与面向对象
运维·python·学习·云计算
小火柴12338 分钟前
利用R绘制条形图
开发语言·r语言
一头生产的驴1 小时前
java整合itext pdf实现固定模版pdf导出
java·python·pdf
魔都吴所谓1 小时前
【python】快速实现pdf批量去除指定位置水印
java·python·pdf
一匹电信狗1 小时前
【MySQL】数据库表的操作
linux·运维·服务器·数据库·mysql·ubuntu·小程序
沐知全栈开发1 小时前
PHP MySQL 插入数据详解
开发语言
YFCodeDream1 小时前
MLLM技术报告 核心创新一览
python·gpt·aigc
自由会客室2 小时前
在 Ubuntu24.04 上安装 JDK 21(Java 21)
java·开发语言