效果很好,具体效果请链接我抖音链接

import random
from math import sin, cos, pi, log
from tkinter import *
import pygame
import time
from PIL import Image, ImageTk
# ======= 爱心函数 =======
HEART_COLOR = "#ff69b4"
def heart_function(t, shrink_ratio, center_x, center_y):
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 += center_x
y += center_y - 30
return int(x), int(y)
def scatter_inside(x, y, center_x, center_y, beta=0.15):
ratio_x = - beta * log(random.random())
ratio_y = - beta * log(random.random())
dx = ratio_x * (x - center_x)
dy = ratio_y * (y - center_y)
return x - dx, y - dy
def shrink(x, y, center_x, center_y, ratio):
force = -1 / (((x - center_x) ** 2 + (y - center_y) ** 2) ** 0.6)
dx = ratio * force * (x - center_x)
dy = ratio * force * (y - center_y)
return x - dx, y - dy
def curve(p):
return 2 * (2 * sin(4 * p)) / (2 * pi)
# ======= 爱心类 =======
class Heart:
def __init__(self, center_x, center_y, shrink_ratio, generate_frame=20):
self.center_x = center_x
self.center_y = center_y
self.shrink_ratio = shrink_ratio
self._points = set()
self._edge_diffusion_points = set()
self._center_diffusion_points = set()
self.all_points = {}
self.build(2800)
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.shrink_ratio, self.center_x, self.center_y)
self._points.add((x, y))
for _x, _y in list(self._points):
for _ in range(3):
x, y = scatter_inside(_x, _y, self.center_x, self.center_y, 0.05)
self._edge_diffusion_points.add((x, y))
point_list = list(self._points)
for _ in range(5500):
x, y = random.choice(point_list)
x, y = scatter_inside(x, y, self.center_x, self.center_y, 0.17)
self._center_diffusion_points.add((x, y))
def calc_position(self, x, y, ratio):
force = 1 / (((x - self.center_x) ** 2 + (y - self.center_y) ** 2) ** 0.520)
dx = ratio * force * (x - self.center_x) + random.randint(-1, 1)
dy = ratio * force * (y - self.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(6 + 6 * (1 + curve(generate_frame / 10 * pi)))
halo_number = int(3800 + 4800 * 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, self.shrink_ratio * 0.91, self.center_x, self.center_y)
x, y = shrink(x, y, self.center_x, self.center_y, halo_radius)
if (x, y) not in heart_halo_point:
heart_halo_point.add((x, y))
x += random.randint(-12, 12)
y += random.randint(-12, 12)
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, 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)
# ======= LOVE雨滴 =======
class LoveRain:
def __init__(self, canvas, width, height, cols=50, speed_range=(4,12), font_size=20):
self.canvas = canvas
self.width = width
self.height = height
self.cols = cols
self.speed_range = speed_range
self.font = ("Courier", font_size, "bold")
self.colors = ["#ff69b4","#ffb6c1","#ffc0cb","#ff1493"]
self.rains = []
self._init_rains()
def _init_rains(self):
gap = self.width // self.cols
for i in range(self.cols):
for _ in range(2):
x = i * gap + gap // 2
y = random.randint(-self.height,0)
speed = random.randint(*self.speed_range)
text = random.choice(["L","O","V","E"])
self.rains.append([x, y, speed, text, random.choice(self.colors)])
def update(self):
for rain in self.rains:
rain[1] += rain[2]
if rain[1] > self.height + 40:
rain[1] = random.randint(-self.height,0)
rain[3] = random.choice(["L","O","V","E"])
rain[4] = random.choice(self.colors)
def draw(self):
for x, y, _, text, color in self.rains:
self.canvas.create_text(x, y, text=text, fill=color, font=self.font)
# ======= 主绘制函数 =======
def draw(main, canvas, heart, start_time, love_rain, photos, photo_start_time, photo_interval, frame=0):
canvas.delete('all')
elapsed = time.time() - start_time
if elapsed < photo_start_time:
love_rain.update()
love_rain.draw()
heart.render(canvas, frame)
canvas.create_text(canvas.winfo_width()//2, canvas.winfo_height()-30,
text="朱举修天天开心", fill="#ffb6c1", font=("Helvetica", max(20, canvas.winfo_width()//40), "bold"))
else:
photo_index = int((elapsed - photo_start_time)//photo_interval)
if photo_index >= len(photos):
photo_index = len(photos)-1
canvas.create_image(canvas.winfo_width()//2, canvas.winfo_height()//2, image=photos[photo_index])
main.after(60, draw, main, canvas, heart, start_time, love_rain, photos, photo_start_time, photo_interval, frame+1)
# ======= 入口 =======
if __name__ == '__main__':
# 初始化音乐
pygame.mixer.init()
pygame.mixer.music.load(r"C:\Users\lenovo\Music\薛之谦 - 演员_H.mp3") # 替换你的音乐路径
pygame.mixer.music.play(-1)
# 创建 Tk 窗口
root = Tk()
root.attributes("-fullscreen", True)
root.bind("<Escape>", lambda e: root.destroy())
SCREEN_WIDTH = root.winfo_screenwidth()
SCREEN_HEIGHT = root.winfo_screenheight()
CANVAS_CENTER_X = SCREEN_WIDTH / 2
CANVAS_CENTER_Y = SCREEN_HEIGHT / 2
IMAGE_ENLARGE = min(SCREEN_WIDTH, SCREEN_HEIGHT) / 57
canvas = Canvas(root, bg='black')
canvas.pack(fill=BOTH, expand=True)
# 创建爱心和雨滴对象
heart = Heart(CANVAS_CENTER_X, CANVAS_CENTER_Y, IMAGE_ENLARGE)
LOVE_FONT_SIZE = max(12, SCREEN_WIDTH // 60)
love_rain = LoveRain(canvas, SCREEN_WIDTH, SCREEN_HEIGHT, font_size=LOVE_FONT_SIZE)
# 加载照片
photo_files = [r"C:\Users\lenovo\Desktop\1.jpg", r"C:\Users\lenovo\Desktop\2.jpg", r"C:\Users\lenovo\Desktop\3.jpg"] # 替换路径
PHOTO_WIDTH = SCREEN_WIDTH // 2
PHOTO_HEIGHT = SCREEN_HEIGHT // 2
photos = []
for f in photo_files:
img = Image.open(f).resize((PHOTO_WIDTH, PHOTO_HEIGHT))
photos.append(ImageTk.PhotoImage(img))
# 照片显示时间
photo_start_time = 261 # 秒(4分21秒)
photo_interval = 3 # 每张照片显示间隔
start_time = time.time()
draw(root, canvas, heart, start_time, love_rain, photos, photo_start_time, photo_interval)
root.mainloop()
817

被折叠的 条评论
为什么被折叠?



