1. 常量定义(参数配置)
CANVAS_WIDTH = 640 # 画布宽度
CANVAS_HEIGHT = 640 # 画布高度
CANVAS_CENTER_X = CANVAS_WIDTH / 2 # 画布中心X坐标
CANVAS_CENTER_Y = CANVAS_HEIGHT / 2 # 画布中心Y坐标
IMAGE_ENLARGE = 50 # 星星大小缩放比例
STAR_COLOR = "#ff3333" # 星星主色(深红色)
BRIGHT_STAR_COLOR = "#ff6666" # 亮星颜色(浅红色)
# 其他参数:控制点的散射、收缩、动画节奏等
数据类(StarParameters)
class StarParameters:
def __init__(self):
self.points = set() # 星星主体点集合
self.edge_diffusion_points = set() # 边缘扩散点集合
self.center_diffusion_points = set() # 中心扩散点集合
self.all_points = {} # 存储每帧的所有点数据(帧号: 点列表)
3. 工具函数(生成与处理点)
这些函数负责计算点的位置,是形成星星形状和动态效果的核心:
star_function(t, shrink_ratio):生成星星轮廓点基于极坐标方程生成星星形状的基础点:
r = 2 * (1 + sin(5 * t)) # 五角星半径方程(5次正弦函数模拟星形)
x = r * cos(t) # 极坐标转直角坐标X
y = r * sin(t) # 极坐标转直角坐标Y
scatter_points(x, y):生成扩散点对基础点进行随机散射,模拟星星边缘的模糊效果:通过对数函数和随机数生成偏移量,使点向四周扩散。shrink_points(x, y, ratio):生成收缩点使点向中心收缩,增强星星的聚集感:基于点到中心的距离计算收缩力,距离越远收缩力越强。curve(p):动画节奏控制函数基于正弦函数生成周期性变化的曲线,控制点的运动节奏,使动画呈现 “收缩 - 扩散” 的循环。
4. 核心逻辑类(Star)
封装了星星的构建、动画计算和渲染逻辑,是程序的核心:
-
__init__初始化创建参数实例,调用build生成基础点,调用calc预计算每帧的点数据。 -
build构建基础点- 生成星星主体点(
points):通过循环调用star_function生成大量点,形成星星轮廓。 - 生成边缘扩散点(
edge_diffusion_points):对主体点进行散射,模拟边缘模糊。 - 生成中心扩散点(
center_diffusion_points):在主体点附近生成更多散射点,增强中心密度。
- 生成星星主体点(
-
calc计算每帧数据为动画的每一帧计算所有点的位置:- 根据
curve函数计算当前帧的缩放比例(ratio),控制点的运动幅度。 - 生成光晕点(
star_halo_point):在星星外围生成随机点,增强层次感。 - 计算主体点、边缘点、中心点的当前位置(加入随机扰动),并存储到
all_points中。
- 根据
-
render渲染当前帧绘制当前帧的所有点:- 从
all_points中获取当前帧的点数据。 - 对每个点绘制矩形(模拟星星),随机使用主色或亮色,实现闪烁效果。
- 从
5. 渲染控制(draw函数与主程序)
-
draw函数:递归调用自身,实现动画循环- 清空画布 → 渲染当前帧 → 延迟后调用下一帧,形成连续动画。
-
主程序:初始化窗口和画布
- 创建 Tkinter 窗口和画布,实例化
Star类,启动draw函数开始动画。
- 创建 Tkinter 窗口和画布,实例化
二、关键技术点
- 数学函数生成形状:通过极坐标方程(
sin(5t))生成五角星轮廓,比直接绘制图形更灵活。 - 点集动画:通过控制大量点的位置变化(散射、收缩)模拟流体效果,避免直接操作复杂图形。
- 预计算帧数据:初始化时计算所有帧的点位置,减少实时计算压力,使动画更流畅。
- 随机化效果:加入随机偏移和颜色变化,模拟自然的星星闪烁和流动感。
三、可优化方向
- 减少点的数量(
NUM_POINTS等参数)可降低 CPU 占用,适合性能较弱的设备。 - 替换
star_function的方程(如改为sin(6t))可生成不同角数的星形。 - 增加颜色渐变参数,可实现星星从中心到边缘的颜色过渡效果。
代码
import random
from math import sin, cos, pi, log
from tkinter import *
# 常量设置(主要修改颜色为红色系)
CANVAS_WIDTH = 640
CANVAS_HEIGHT = 640
CANVAS_CENTER_X = CANVAS_WIDTH / 2
CANVAS_CENTER_Y = CANVAS_HEIGHT / 2
IMAGE_ENLARGE = 50 # 缩放比例
STAR_COLOR = "#ff3333" # 红色主色(较深)
BRIGHT_STAR_COLOR = "#ff6666" # 亮红色(较浅)
SCATTER_BETA = 0.15
SHRINK_RATIO = 10
CURVE_RATIO = 8
FRAME_DELAY = 160
NUM_POINTS = 1200
NUM_HALO_POINTS = 1800
class StarParameters:
def __init__(self):
self.points = set()
self.edge_diffusion_points = set()
self.center_diffusion_points = set()
self.all_points = {}
# 星星形状函数
def star_function(t, shrink_ratio=IMAGE_ENLARGE):
r = 2 * (1 + sin(5 * t))
x = r * cos(t)
y = r * sin(t)
x *= shrink_ratio
y *= shrink_ratio
x = max(0, min(CANVAS_WIDTH, x + CANVAS_CENTER_X))
y = max(0, min(CANVAS_HEIGHT, y + CANVAS_CENTER_Y))
return int(x), int(y)
def scatter_points(x, y, beta=SCATTER_BETA):
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)
x_new = max(0, min(CANVAS_WIDTH, x - dx))
y_new = max(0, min(CANVAS_HEIGHT, y - dy))
return x_new, y_new
def shrink_points(x, y, ratio):
force = -1 / (((x - CANVAS_CENTER_X) ** 2 + (y - CANVAS_CENTER_Y) ** 2) ** 0.6 + 1e-6)
dx = ratio * force * (x - CANVAS_CENTER_X)
dy = ratio * force * (y - CANVAS_CENTER_Y)
return x - dx, y - dy
def curve(p):
return 2 * (3 * sin(4 * p)) / (2 * pi)
class Star:
def __init__(self, generate_frame=20):
self.parameters = StarParameters()
self.generate_frame = generate_frame
self.build(NUM_POINTS)
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 = star_function(t)
self.parameters.points.add((x, y))
for _x, _y in list(self.parameters.points):
for _ in range(2):
x, y = scatter_points(_x, _y)
self.parameters.edge_diffusion_points.add((x, y))
point_list = list(self.parameters.points)
for _ in range(NUM_HALO_POINTS):
x, y = random.choice(point_list)
x, y = scatter_points(x, y, 0.17)
self.parameters.center_diffusion_points.add((x, y))
@staticmethod
def calc_position(x, y, ratio):
force_denominator = ((x - CANVAS_CENTER_X) ** 2 + (y - CANVAS_CENTER_Y) ** 2) ** 0.520 + 1e-6
force = 1 / force_denominator
dx = ratio * force * (x - CANVAS_CENTER_X) + random.randint(-1, 1)
dy = ratio * force * (y - CANVAS_CENTER_Y) + random.randint(-1, 1)
x_new = max(0, min(CANVAS_WIDTH, x - dx))
y_new = max(0, min(CANVAS_HEIGHT, y - dy))
return x_new, y_new
def calc(self, generate_frame):
ratio = CURVE_RATIO * curve(generate_frame / 10 * pi)
halo_radius = int(4 + 6 * (1 + curve(generate_frame / 10 * pi)))
halo_number = int(2000 + 3000 * abs(curve(generate_frame / 10 * pi) ** 2))
all_points = []
star_halo_point = set()
for _ in range(halo_number):
t = random.uniform(0, 2 * pi)
x, y = star_function(t, shrink_ratio=SHRINK_RATIO)
x, y = shrink_points(x, y, halo_radius)
if (x, y) not in star_halo_point:
star_halo_point.add((x, y))
x += random.randint(-10, 10)
y += random.randint(-10, 10)
size = random.choice((1, 2))
all_points.append((x, y, size))
for x, y in self.parameters.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.parameters.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.parameters.center_diffusion_points:
x, y = self.calc_position(x, y, ratio)
size = 1
all_points.append((x, y, size))
self.parameters.all_points[generate_frame] = all_points
def render(self, render_canvas, render_frame):
try:
points = self.parameters.all_points[render_frame % self.generate_frame]
except KeyError:
points = []
for x, y, size in points:
if 0 <= x < CANVAS_WIDTH and 0 <= y < CANVAS_HEIGHT:
# 红色系闪烁效果
color = BRIGHT_STAR_COLOR if random.random() > 0.7 else STAR_COLOR
render_canvas.create_rectangle(x, y, x + size, y + size, width=0, fill=color)
def draw(main: Tk, render_canvas: Canvas, render_star: Star, render_frame=0):
render_canvas.delete('all')
render_star.render(render_canvas, render_frame)
main.after(FRAME_DELAY, draw, main, render_canvas, render_star, render_frame + 1)
if __name__ == '__main__':
root = Tk()
root.title('红色动态星星')
canvas = Canvas(root, bg='black', height=CANVAS_HEIGHT, width=CANVAS_WIDTH)
canvas.pack()
try:
star = Star()
draw(root, canvas, star)
root.mainloop()
except Exception as e:
print(f"运行错误: {e}")
root.mainloop()