目录
[版本1 动态粒子爱心](#版本1 动态粒子爱心)

功能概括
使用 pygame
实现了一个动态的 心形粒子特效动画。主要功能包括:
-
在屏幕中心通过心形参数方程生成粒子轨迹;
-
每个粒子具有独立的大小、速度、角度、寿命和随机扰动;
-
粒子逐渐缩小并淡出,营造浪漫柔和的视觉效果;
-
采用
asyncio
实现异步主循环,保持帧率稳定在 60 FPS; -
自动循环更新粒子,实现持续动态效果;
-
可兼容 Web 环境(通过对
platform.system()
判断Emscripten
进行适配)。
【运行效果】
安装依赖
python
pip install pygame
版本1 动态粒子爱心
执行代码
python
import asyncio
import platform
import pygame
import random
import math
# 初始化Pygame
pygame.init()
# 屏幕设置
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Enhanced Heart Particle Effect")
# 颜色定义
WHITE = (255, 255, 255)
RED = (255, 0, 0)
PINK = (255, 192, 203)
DARK_PINK = (255, 105, 180)
# 粒子类
class Particle:
def __init__(self, x, y):
self.x = x
self.y = y
self.size = random.uniform(3, 6)
self.color = random.choice([PINK, DARK_PINK, RED])
self.speed = random.uniform(0.3, 1.5)
self.angle = random.uniform(0, 2 * math.pi)
self.lifetime = random.randint(80, 160)
self.age = 0
self.offset_x = random.uniform(-5, 5)
self.offset_y = random.uniform(-5, 5)
def update(self):
# 心形路径的参数方程
t = self.angle
scale = 18 # 放大心形
base_x = scale * 16 * (math.sin(t) ** 3)
base_y = -(scale * (13 * math.cos(t) - 5 * math.cos(2 * t) - 2 * math.cos(3 * t) - math.cos(4 * t)))
# 平滑移动到目标位置
self.x += (WIDTH / 2 + base_x + self.offset_x - self.x) * 0.1
self.y += (HEIGHT / 2 + base_y + self.offset_y - self.y) * 0.1
self.age += 1
self.size = max(1, self.size * 0.985) # 更慢的缩小速度
self.angle += self.speed * 0.02 # 缓慢旋转
def draw(self, surface):
if self.age < self.lifetime:
alpha = max(0, 255 * (1 - self.age / self.lifetime))
color = (*self.color[:3], int(alpha))
pygame.draw.circle(surface, color, (int(self.x), int(self.y)), int(self.size))
# 粒子列表
particles = []
def setup():
global particles
particles = [Particle(WIDTH / 2, HEIGHT / 2) for _ in range(400)] # 增加粒子数量
def update_loop():
global particles
screen.fill((10, 10, 20)) # 深色背景增强对比
# 更新和绘制粒子
for particle in particles[:]:
particle.update()
particle.draw(screen)
if particle.age >= particle.lifetime:
particles.remove(particle)
particles.append(Particle(WIDTH / 2, HEIGHT / 2))
pygame.display.flip()
async def main():
setup()
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
return
update_loop()
clock.tick(60)
await asyncio.sleep(1.0 / 60)
if platform.system() == "Emscripten":
asyncio.ensure_future(main())
else:
if __name__ == "__main__":
asyncio.run(main())
运行效果
动态效果:动态粒子爱心-CSDN直播

版本2:优化粒子点缀
执行代码
python
import asyncio
import platform
import pygame
import random
import math
# 初始化Pygame
pygame.init()
# 屏幕设置
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Enhanced Heart Particle Effect")
# 颜色定义
WHITE = (255, 255, 255)
RED = (255, 0, 0)
PINK = (255, 192, 203)
DARK_PINK = (255, 105, 180)
SOFT_PINK = (255, 182, 193)
# 粒子类
class Particle:
def __init__(self, x, y, is_decorative=False):
self.x = x
self.y = y
self.is_decorative = is_decorative
self.size = random.uniform(2, 5) if is_decorative else random.uniform(3, 7)
self.color = random.choice([PINK, DARK_PINK, RED, SOFT_PINK])
self.speed = random.uniform(0.2, 1.2) if not is_decorative else random.uniform(0.1, 0.5)
self.angle = random.uniform(0, 2 * math.pi)
self.lifetime = random.randint(100, 200) if not is_decorative else random.randint(50, 150)
self.age = 0
self.offset_x = random.uniform(-8, 8) if not is_decorative else random.uniform(-50, 50)
self.offset_y = random.uniform(-8, 8) if not is_decorative else random.uniform(-50, 50)
def update(self):
if not self.is_decorative:
# 心形路径的参数方程
t = self.angle
scale = 20 # 放大心形
base_x = scale * 16 * (math.sin(t) ** 3)
base_y = -(scale * (13 * math.cos(t) - 5 * math.cos(2 * t) - 2 * math.cos(3 * t) - math.cos(4 * t)))
# 平滑移动到目标位置
self.x += (WIDTH / 2 + base_x + self.offset_x - self.x) * 0.15
self.y += (HEIGHT / 2 + base_y + self.offset_y - self.y) * 0.15
else:
# 点缀粒子随机漂浮
self.x += random.uniform(-1, 1)
self.y += random.uniform(-1, 1)
self.x = max(0, min(WIDTH, self.x))
self.y = max(0, min(HEIGHT, self.y))
self.age += 1
self.size = max(1, self.size * 0.98) # 更慢的缩小速度
self.angle += self.speed * 0.015 # 更慢的旋转
def draw(self, surface):
if self.age < self.lifetime:
alpha = max(0, 255 * (1 - self.age / self.lifetime))
color = (*self.color[:3], int(alpha))
pygame.draw.circle(surface, color, (int(self.x), int(self.y)), int(self.size))
# 粒子列表
particles = []
def setup():
global particles
# 主心形粒子
particles = [Particle(WIDTH / 2, HEIGHT / 2) for _ in range(600)] # 增加粒子数量
# 点缀粒子
particles.extend([Particle(random.randint(0, WIDTH), random.randint(0, HEIGHT), is_decorative=True) for _ in range(200)])
def update_loop():
global particles
screen.fill((10, 10, 20)) # 深色背景增强对比
# 更新和绘制粒子
for particle in particles[:]:
particle.update()
particle.draw(screen)
if particle.age >= particle.lifetime:
particles.remove(particle)
if particle.is_decorative:
particles.append(Particle(random.randint(0, WIDTH), random.randint(0, HEIGHT), is_decorative=True))
else:
particles.append(Particle(WIDTH / 2, HEIGHT / 2))
pygame.display.flip()
async def main():
setup()
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
return
update_loop()
clock.tick(60)
await asyncio.sleep(1.0 / 60)
if platform.system() == "Emscripten":
asyncio.ensure_future(main())
else:
if __name__ == "__main__":
asyncio.run(main())
运行效果

版本3:增加例子密度
执行代码
python
import asyncio
import platform
import pygame
import random
import math
# 初始化Pygame
pygame.init()
# 屏幕设置
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Dense 3D Heart Particle Effect")
# 颜色定义
WHITE = (255, 255, 255)
RED = (255, 0, 0)
PINK = (255, 192, 203)
DARK_PINK = (255, 105, 180)
SOFT_PINK = (255, 182, 193)
# 粒子类
class Particle:
def __init__(self, x, y, is_decorative=False):
self.x = x
self.y = y
self.z = random.uniform(-50, 50) if not is_decorative else random.uniform(-100, 100)
self.is_decorative = is_decorative
self.size = random.uniform(2, 5) if is_decorative else random.uniform(2, 7) # 调整主粒子大小
self.color = random.choice([PINK, DARK_PINK, RED, SOFT_PINK])
self.speed = random.uniform(0.2, 1.2) if not is_decorative else random.uniform(0.1, 0.5)
self.angle = random.uniform(0, 2 * math.pi)
self.lifetime = random.randint(100, 200) if not is_decorative else random.randint(50, 150)
self.age = 0
self.offset_x = random.uniform(-8, 8) if not is_decorative else random.uniform(-50, 50)
self.offset_y = random.uniform(-8, 8) if not is_decorative else random.uniform(-50, 50)
self.offset_z = random.uniform(-10, 10) if not is_decorative else random.uniform(-20, 20)
def update(self):
if not self.is_decorative:
# 心形路径的参数方程
t = self.angle
scale = 20
base_x = scale * 16 * (math.sin(t) ** 3)
base_y = scale * (13 * math.cos(t) - 5 * math.cos(2 * t) - 2 * math.cos(3 * t) - math.cos(4 * t))
# 透视投影
fov = 200
scale_factor = fov / (fov + self.z) if (fov + self.z) != 0 else 1
proj_x = (base_x + self.offset_x) * scale_factor
proj_y = (base_y + self.offset_y) * scale_factor
# 平滑移动
self.x += (WIDTH / 2 + proj_x - self.x) * 0.15
self.y += (HEIGHT / 2 - proj_y - self.y) * 0.15
self.z += (self.offset_z - self.z) * 0.1
else:
# 点缀粒子随机漂浮
self.x += random.uniform(-1, 1)
self.y += random.uniform(-1, 1)
self.z += random.uniform(-0.5, 0.5)
self.x = max(0, min(WIDTH, self.x))
self.y = max(0, min(HEIGHT, self.y))
self.z = max(-100, min(100, self.z))
self.age += 1
self.size = max(1, self.size * 0.98)
self.angle += self.speed * 0.015
def draw(self, surface):
if self.age < self.lifetime:
# 计算透视缩放
fov = 200
scale_factor = fov / (fov + self.z) if (fov + self.z) != 0 else 1
adjusted_size = max(1, self.size * scale_factor)
alpha = max(0, int(255 * (1 - self.age / self.lifetime) * scale_factor))
# 调整亮度,确保整数并在[0, 255]范围内
brightness = max(0.5, scale_factor)
color = (
min(255, max(0, int(self.color[0] * brightness))),
min(255, max(0, int(self.color[1] * brightness))),
min(255, max(0, int(self.color[2] * brightness)))
)
# 使用透明表面绘制
temp_surface = pygame.Surface((int(adjusted_size * 2), int(adjusted_size * 2)), pygame.SRCALPHA)
pygame.draw.circle(temp_surface, color, (int(adjusted_size), int(adjusted_size)), int(adjusted_size))
temp_surface.set_alpha(alpha)
surface.blit(temp_surface, (int(self.x - adjusted_size), int(self.y - adjusted_size)))
# 粒子列表
particles = []
def setup():
global particles
particles = [Particle(WIDTH / 2, HEIGHT / 2) for _ in range(1000)] # 增加到1000个主粒子
particles.extend([Particle(random.randint(0, WIDTH), random.randint(0, HEIGHT), is_decorative=True) for _ in range(200)])
def update_loop():
global particles
screen.fill((10, 10, 20))
# 按z轴排序
particles.sort(key=lambda p: p.z, reverse=True)
# 更新和绘制
for particle in particles[:]:
particle.update()
particle.draw(screen)
if particle.age >= particle.lifetime:
particles.remove(particle)
if particle.is_decorative:
particles.append(Particle(random.randint(0, WIDTH), random.randint(0, HEIGHT), is_decorative=True))
else:
particles.append(Particle(WIDTH / 2, HEIGHT / 2))
pygame.display.flip()
async def main():
setup()
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
return
update_loop()
clock.tick(60)
await asyncio.sleep(1.0 / 60)
if platform.system() == "Emscripten":
asyncio.ensure_future(main())
else:
if __name__ == "__main__":
asyncio.run(main())
运行效果

版本4:改成粉色粒子
执行代码
python
import asyncio
import platform
import pygame
import random
import math
import colorsys
# 初始化Pygame
pygame.init()
# 屏幕设置
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Bright Romantic 3D Heart Particle Effect")
# 颜色定义
WHITE = (255, 255, 255)
SOFT_PINK = (255, 182, 193)
# 创意参数
PULSE_INTERVAL = 5000 # 脉冲间隔(毫秒)
EXPLOSION_INTERVAL = 5000 # 爆炸间隔(毫秒)
last_pulse = 0
last_explosion = 0
explosion_particles = []
# 粒子类
class Particle:
def __init__(self, x, y, is_decorative=False, is_explosion=False):
self.x = x
self.y = y
self.z = random.uniform(-50, 50) if not is_decorative else random.uniform(-100, 100)
self.is_decorative = is_decorative
self.is_explosion = is_explosion
self.size = random.uniform(2, 5) if is_decorative else random.uniform(2, 7) if not is_explosion else random.uniform(1, 3)
self.color_hue = random.uniform(0.9, 0.1) # 限制到粉红-红色-紫色区间
self.speed = random.uniform(0.2, 1.2) if not (is_decorative or is_explosion) else random.uniform(0.1, 0.5)
self.angle = random.uniform(0, 2 * math.pi)
self.lifetime = random.randint(100, 200) if not is_decorative else random.randint(50, 150) if not is_explosion else random.randint(30, 60)
self.age = 0
self.offset_x = random.uniform(-8, 8) if not (is_decorative or is_explosion) else random.uniform(-50, 50) if is_decorative else random.uniform(-10, 10)
self.offset_y = random.uniform(-8, 8) if not (is_decorative or is_explosion) else random.uniform(-50, 50) if is_decorative else random.uniform(-10, 10)
self.offset_z = random.uniform(-10, 10) if not (is_decorative or is_explosion) else random.uniform(-20, 20) if is_decorative else random.uniform(-5, 5)
def update(self):
if self.is_explosion:
# 爆炸粒子向外扩散
self.x += self.offset_x * 0.1
self.y += self.offset_y * 0.1
self.z += self.offset_z * 0.05
self.size *= 0.95 # 逐渐缩小
elif not self.is_decorative:
# 心形路径
t = self.angle
scale = 20
base_x = scale * 16 * (math.sin(t) ** 3)
base_y = scale * (13 * math.cos(t) - 5 * math.cos(2 * t) - 2 * math.cos(3 * t) - math.cos(4 * t))
fov = 200
scale_factor = fov / (fov + self.z) if (fov + self.z) != 0 else 1
proj_x = (base_x + self.offset_x) * scale_factor
proj_y = (base_y + self.offset_y) * scale_factor
self.x += (WIDTH / 2 + proj_x - self.x) * 0.15
self.y += (HEIGHT / 2 - proj_y - self.y) * 0.15
self.z += (self.offset_z - self.z) * 0.1
self.angle += self.speed * 0.015
else:
# 点缀粒子
self.x += random.uniform(-1, 1)
self.y += random.uniform(-1, 1)
self.z += random.uniform(-0.5, 0.5)
self.x = max(0, min(WIDTH, self.x))
self.y = max(0, min(HEIGHT, self.y))
self.z = max(-100, min(100, self.z))
self.age += 1
self.size = max(1, self.size * 0.98)
def draw(self, surface):
if self.age < self.lifetime:
fov = 200
scale_factor = fov / (fov + self.z) if (fov + self.z) != 0 else 1
adjusted_size = max(1, self.size * scale_factor)
alpha = max(0, int(255 * (1 - self.age / self.lifetime) * scale_factor))
# 彩虹色渐变,限定爱心色调,增加亮度
hue = (self.color_hue + pygame.time.get_ticks() / 10000) % 0.2 + 0.9 # 循环在0.9-1.1范围内
color = colorsys.hsv_to_rgb(hue % 1, 0.4, 0.95) # 亮度提升到0.95
color = (int(color[0] * 255), int(color[1] * 255), int(color[2] * 255))
temp_surface = pygame.Surface((int(adjusted_size * 2), int(adjusted_size * 2)), pygame.SRCALPHA)
pygame.draw.circle(temp_surface, color, (int(adjusted_size), int(adjusted_size)), int(adjusted_size))
temp_surface.set_alpha(alpha)
surface.blit(temp_surface, (int(self.x - adjusted_size), int(self.y - adjusted_size)))
# 粒子列表
particles = []
def setup():
global particles
particles = [Particle(WIDTH / 2, HEIGHT / 2) for _ in range(1000)]
particles.extend([Particle(random.randint(0, WIDTH), random.randint(0, HEIGHT), is_decorative=True) for _ in range(200)])
def update_loop():
global particles, last_pulse, last_explosion, explosion_particles
current_time = pygame.time.get_ticks()
# 绘制动态光晕
if current_time - last_pulse > PULSE_INTERVAL:
last_pulse = current_time
pulse_radius = 50 * (1 + 0.5 * math.sin(current_time / 500)) # 心跳脉冲
for r in range(int(pulse_radius), 0, -1):
alpha = int(150 * (r / pulse_radius)) # 增加光晕亮度
color = (255, 182, 193, alpha) # 柔和粉色
pygame.draw.circle(screen, color[:3], (WIDTH // 2, HEIGHT // 2), r, 1)
# 触发粒子爆炸
if current_time - last_explosion > EXPLOSION_INTERVAL:
last_explosion = current_time
for _ in range(100):
explosion_particles.append(Particle(WIDTH // 2, HEIGHT // 2, is_explosion=True))
screen.fill((10, 10, 20))
# 更新和绘制爆炸粒子
for p in explosion_particles[:]:
p.update()
p.draw(screen)
if p.age >= p.lifetime:
explosion_particles.remove(p)
# 按z轴排序
all_particles = particles + explosion_particles
all_particles.sort(key=lambda p: p.z, reverse=True)
# 更新和绘制
for particle in all_particles[:]:
particle.update()
particle.draw(screen)
if particle.age >= particle.lifetime and particle in particles:
particles.remove(particle)
if particle.is_decorative:
particles.append(Particle(random.randint(0, WIDTH), random.randint(0, HEIGHT), is_decorative=True))
else:
particles.append(Particle(WIDTH / 2, HEIGHT // 2))
pygame.display.flip()
async def main():
setup()
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
return
update_loop()
clock.tick(60)
await asyncio.sleep(1.0 / 60)
if platform.system() == "Emscripten":
asyncio.ensure_future(main())
else:
if __name__ == "__main__":
asyncio.run(main())
运行效果
