目录

第一种:完全不一样版

效果图:

代码:

第二种:偷懒版(其实就是放了一张动态图) 

效果图:

代码: 

第三种:Python版 

效果图1:

代码1:

效果图2:

代码2:


第一种:完全不一样版

效果图:

代码:

链接:https://pan.baidu/s/1mEYbXMu7S6xW0-lUhSsKsw?pwd=eftm 
提取码:eftm 

第二种:偷懒版(其实就是放了一张动态图) 

效果图:

代码: 

链接:https://pan.baidu/s/1hc3pgmK_oFR2oSz2XCccWA?pwd=sxbu 
提取码:sxbu 

第三种:Python版 

效果图1:

代码1:

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 / 2

IMAGE_ENLARGE = 11  # 放大比例

HEART_COLOR = "#ff1943"


def heart_function(t, shrink_ratio: float = IMAGE_ENLARGE):
    """

    “爱心函数生成器”

    :param shrink_ratio: 放大比例

    :param t: 参数

    :return: 坐标

    """

    # 基础函数

    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 scatter_inside(x, y, beta=0.15):
    """

    随机内部扩散

    :param x: 原x

    :param y: 原y

    :param beta: 强度

    :return: 新坐标

    """

    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


def shrink(x, y, ratio):
    """

    抖动

    :param x: 原x

    :param y: 原y

    :param ratio: 比例

    :return: 新坐标

    """

    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 curve(p):
    """

    自定义曲线函数,调整跳动周期

    :param p: 参数

    :return: 正弦

    """

    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(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 - 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 - dy

    def calc(self, generate_frame):

        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 = []

        # 光环

        heart_halo_point = set()  # 光环的点坐标集合

        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))

        # 轮廓

        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(160, draw, main, render_canvas, render_heart, render_frame + 1)


if __name__ == '__main__':
    root = Tk()  # 一个Tk

    canvas = Canvas(root, bg='black', height=CANVAS_HEIGHT, width=CANVAS_WIDTH)

    canvas.pack()

    heart = Heart()  # 心

    draw(root, canvas, heart)

    root.mainloop()

效果图2:

代码2:

from math import cos, pi
import numpy as np
import cv2


class HeartSignal:
    def __init__(self, frame_num=20, seed_points_num=2000, seed_num=None, frame_width=1080, frame_height=960,
                 scale=10.1):
        super().__init__()
        self.frame_width = frame_width
        self.frame_height = frame_height
        self.center_x = self.frame_width / 2
        self.center_y = self.frame_height / 2

        self._points = set()  # 主图坐标点
        self._edge_diffusion_points = set()  # 边缘扩散效果点坐标集合
        self._center_diffusion_points = set()  # 中心扩散效果点坐标集合
        self._heart_halo_point = set()  # 光晕效果坐标集合
        self.frame_points = []  # 每帧动态点坐标
        self.frame_num = frame_num
        self.seed_num = seed_num
        self.seed_points_num = seed_points_num
        self.scale = scale

    def heart_function(self, t, frame_idx=0, scale=5.20):
        """
        图形方程
        :param frame_idx: 帧的索引,根据帧数变换心形
        :param scale: 放大比例
        :param t: 参数
        :return: 坐标
        """
        trans = 3
        trans = 3 - (1 + self.curve(frame_idx, self.frame_num)) * 0.5  # 改变心形饱满度度的参数

        x = 15 * (np.sin(t) ** 3)
        t = np.where((pi < t) & (t < 2 * pi), 2 * pi - t, t)  # 翻转x > 0部分的图形到3、4象限
        y = -(14 * np.cos(t) - 4 * np.cos(2 * t) - 2 * np.cos(3 * t) - np.cos(trans * t))

        ign_area = 0.15
        center_ids = np.where((x > -ign_area) & (x < ign_area))
        if np.random.random() > 0.32:
            x, y = np.delete(x, center_ids), np.delete(y, center_ids)  # 删除稠密部分的扩散,为了美观

        # 放大
        x *= scale
        y *= scale

        # 移到画布中央
        x += self.center_x
        y += self.center_y

        # 原心形方程
        # x = 15 * (sin(t) ** 3)
        # y = -(14 * cos(t) - 4 * cos(2 * t) - 2 * cos(3 * t) - cos(3 * t))
        return x.astype(int), y.astype(int)

    def butterfly_function(self, t, frame_idx=0, scale=64):
        """
        图形函数
        :param frame_idx:
        :param scale: 放大比例
        :param t: 参数
        :return: 坐标
        """
        # 基础函数
        # x = 15 * (sin(t) ** 3)
        # y = -(14 * cos(t) - 4 * cos(2 * t) - 2 * cos(3 * t) - cos(3 * t))
        t = t * pi
        p = np.exp(np.sin(t)) - 2.5 * np.cos(4 * t) + np.sin(t) ** 5
        x = p * np.cos(t)
        y = - p * np.sin(t)

        # 放大
        x *= scale
        y *= scale

        # 移到画布中央
        x += self.center_x
        y += self.center_y

        return x.astype(int), y.astype(int)

    def shrink(self, x, y, ratio, offset=1, p=0.5, dist_func="uniform"):
        """
        带随机位移的抖动
        :param x: 原x
        :param y: 原y
        :param ratio: 缩放比例
        :param p:
        :param offset:
        :return: 转换后的x,y坐标
        """
        x_ = (x - self.center_x)
        y_ = (y - self.center_y)
        force = 1 / ((x_ ** 2 + y_ ** 2) ** p + 1e-30)

        dx = ratio * force * x_
        dy = ratio * force * y_

        def d_offset(x):
            if dist_func == "uniform":
                return x + np.random.uniform(-offset, offset, size=x.shape)
            elif dist_func == "norm":
                return x + offset * np.random.normal(0, 1, size=x.shape)

        dx, dy = d_offset(dx), d_offset(dy)

        return x - dx, y - dy

    def scatter(self, x, y, alpha=0.75, beta=0.15):
        """
        随机内部扩散的坐标变换
        :param alpha: 扩散因子 - 松散
        :param x: 原x
        :param y: 原y
        :param beta: 扩散因子 - 距离
        :return: x,y 新坐标
        """

        ratio_x = - beta * np.log(np.random.random(x.shape) * alpha)
        ratio_y = - beta * np.log(np.random.random(y.shape) * alpha)
        dx = ratio_x * (x - self.center_x)
        dy = ratio_y * (y - self.center_y)

        return x - dx, y - dy

    def curve(self, x, x_num):
        """
        跳动周期曲线
        :param p: 参数
        :return: y
        """

        # 可以尝试换其他的动态函数,达到更有力量的效果(贝塞尔?)
        def ori_func(t):
            return cos(t)

        func_period = 2 * pi
        return ori_func(x / x_num * func_period)

    def gen_points(self, points_num, frame_idx, shape_func):
        # 用周期函数计算得到一个因子,用到所有组成部件上,使得各个部分的变化周期一致
        cy = self.curve(frame_idx, self.frame_num)
        ratio = 10 * cy

        # 图形
        seed_points = np.linspace(0, 2 * pi, points_num)
        seed_x, seed_y = shape_func(seed_points, frame_idx, scale=self.scale)
        x, y = self.shrink(seed_x, seed_y, ratio, offset=2)
        point_size = np.random.choice([1, 2], x.shape, replace=True, p=[0.5, 0.5])
        tag = np.ones_like(x)

        def delete_points(x_, y_, ign_area, ign_prop):
            ign_area = ign_area
            center_ids = np.where((x_ > self.center_x - ign_area) & (x_ < self.center_x + ign_area))
            center_ids = center_ids[0]
            np.random.shuffle(center_ids)
            del_num = round(len(center_ids) * ign_prop)
            del_ids = center_ids[:del_num]
            x_, y_ = np.delete(x_, del_ids), np.delete(y_, del_ids)  # 删除稠密部分的扩散,为了美观
            return x_, y_

        # 多层次扩散
        for idx, beta in enumerate(np.linspace(0.05, 0.2, 6)):
            alpha = 1 - beta
            x_, y_ = self.scatter(seed_x, seed_y, alpha, beta)
            x_, y_ = self.shrink(x_, y_, ratio, offset=round(beta * 15))
            x = np.concatenate((x, x_), 0)
            y = np.concatenate((y, y_), 0)
            p_size = np.random.choice([1, 2], x_.shape, replace=True, p=[0.55 + beta, 0.45 - beta])
            point_size = np.concatenate((point_size, p_size), 0)
            tag_ = np.ones_like(x_) * 2
            tag = np.concatenate((tag, tag_), 0)

        # 光晕
        halo_ratio = int(7 + 2 * abs(cy))  # 收缩比例随周期变化

        # 基础光晕
        x_, y_ = shape_func(seed_points, frame_idx, scale=self.scale + 0.9)
        x_1, y_1 = self.shrink(x_, y_, halo_ratio, offset=18, dist_func="uniform")
        x_1, y_1 = delete_points(x_1, y_1, 20, 0.5)
        x = np.concatenate((x, x_1), 0)
        y = np.concatenate((y, y_1), 0)

        # 炸裂感光晕
        halo_number = int(points_num * 0.6 + points_num * abs(cy))  # 光晕点数也周期变化
        seed_points = np.random.uniform(0, 2 * pi, halo_number)
        x_, y_ = shape_func(seed_points, frame_idx, scale=self.scale + 0.9)
        x_2, y_2 = self.shrink(x_, y_, halo_ratio, offset=int(6 + 15 * abs(cy)), dist_func="norm")
        x_2, y_2 = delete_points(x_2, y_2, 20, 0.5)
        x = np.concatenate((x, x_2), 0)
        y = np.concatenate((y, y_2), 0)

        # 膨胀光晕
        x_3, y_3 = shape_func(np.linspace(0, 2 * pi, int(points_num * .4)),
                              frame_idx, scale=self.scale + 0.2)
        x_3, y_3 = self.shrink(x_3, y_3, ratio * 2, offset=6)
        x = np.concatenate((x, x_3), 0)
        y = np.concatenate((y, y_3), 0)

        halo_len = x_1.shape[0] + x_2.shape[0] + x_3.shape[0]
        p_size = np.random.choice([1, 2, 3], halo_len, replace=True, p=[0.7, 0.2, 0.1])
        point_size = np.concatenate((point_size, p_size), 0)
        tag_ = np.ones(halo_len) * 2 * 3
        tag = np.concatenate((tag, tag_), 0)

        x_y = np.around(np.stack([x, y], axis=1), 0)
        x, y = x_y[:, 0], x_y[:, 1]
        return x, y, point_size, tag

    def get_frames(self, shape_func):
        for frame_idx in range(frame_num):
            np.random.seed(self.seed_num)
            self.frame_points.append(self.gen_points(self.seed_points_num, frame_idx, shape_func))

        frames = []

        def add_points(frame, x, y, size, tag):
            # white = np.array([255, 255, 255], dtype='uint8')
            # dark_red = np.array([250, 90, 90], dtype='uint8')
            purple = np.array([180, 87, 200], dtype='uint8')  # 180, 87, 200
            light_pink = np.array([228, 140, 140], dtype='uint8')  # [228, 140, 140]
            rose_pink = np.array([228, 100, 100], dtype='uint8')

            x, y = x.astype(int), y.astype(int)
            frame[y, x] = rose_pink

            size_2 = np.int64(size == 2)
            frame[y, x + size_2] = rose_pink
            frame[y + size_2, x] = rose_pink

            size_3 = np.int64(size == 3)
            frame[y + size_3, x] = rose_pink
            frame[y - size_3, x] = rose_pink
            frame[y, x + size_3] = rose_pink
            frame[y, x - size_3] = rose_pink
            frame[y + size_3, x + size_3] = rose_pink
            frame[y - size_3, x - size_3] = rose_pink
            # frame[y - size_3, x + size_3] = color
            # frame[y + size_3, x - size_3] = color

            # 高光
            random_sample = np.random.choice([1, 0], size=tag.shape, p=[0.3, 0.7])

            # tag2_size1 = np.int64((tag <= 2) & (size == 1) & (random_sample == 1))
            # frame[y * tag2_size1, x * tag2_size1] = light_pink

            tag2_size2 = np.int64((tag <= 2) & (size == 2) & (random_sample == 1))
            frame[y * tag2_size2, x * tag2_size2] = purple
            # frame[y * tag2_size2, (x + 1) * tag2_size2] = light_pink
            # frame[(y + 1) * tag2_size2, x * tag2_size2] = light_pink
            frame[(y + 1) * tag2_size2, (x + 1) * tag2_size2] = light_pink

            # frame[y * tag2_size2, x * tag2_size2] = light_pink
            # frame[y, x + tag2_size2] = light_pink
            # frame[y + tag2_size2, x] = light_pink
            # frame[y + tag2_size2, x + tag2_size2] = light_pink

        for x, y, size, tag in self.frame_points:
            frame = np.zeros([self.frame_height, self.frame_width, 3], dtype="uint8")
            add_points(frame, x, y, size, tag)
            frames.append(frame)

        return frames

    def draw(self, wait, shape_func):
        frames = self.get_frames(shape_func)
        # while True:
        # n = 20
        while True:
            for frame in frames:
                show_frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
                show_frame = cv2.resize(show_frame, (self.frame_width, self.frame_height))
                cv2.imshow("Love U", show_frame)
                cv2.waitKey(wait)


if __name__ == '__main__':
    period_time = 1000 * 1.5  # 1.5s一个周期
    frame_num = 30
    wait = int(period_time / frame_num)
    heart = HeartSignal(frame_num=frame_num, seed_points_num=2000, seed_num=5201314, frame_width=720, frame_height=640,
                        scale=10.1)
    heart.draw(wait, heart.heart_function)

    # 蝴蝶,取消下面两行注释,注释掉上面两行
    # heart = HeartSignal(frame_num=frame_num, seed_points_num=2000, seed_num=5201314, frame_width=800, frame_height=720,
    #                     scale=60)
    # heart.draw(wait, heart.butterfly_function)
    # pass

更多推荐

李珣的爱心代码