请求解析Python中Matplotlib正弦曲线动画脚本的工作原理
正弦曲线联动动画代码详解
这段Python代码用Matplotlib实现了单位圆与正弦曲线联动的动态效果:左侧单位圆上的红点随角度移动,右侧同步绘制对应角度的正弦曲线,二者用虚线实时连接,最终导出为5秒、60fps的MP4动画。下面拆解各部分的作用:
1. 依赖导入与路径配置
import matplotlib.pyplot as plt import numpy as np import matplotlib.animation as animation from matplotlib.patches import ConnectionPatch import os desktop_path = os.path.join(os.path.expanduser("~"), "Desktop")
- 导入的库各司其职:
plt负责绘图基础操作,numpy处理数学计算,animation实现动画核心逻辑,ConnectionPatch绘制跨子图的虚线,os处理文件路径 desktop_path:获取当前用户的桌面路径,后续将生成的动画保存在该位置
2. 绘图布局初始化
fig, (axl, axr) = plt.subplots( ncols=2, sharey=True, figsize=(12, 4), gridspec_kw=dict(width_ratios=[1, 3], wspace=0.1), ) axl.set_aspect(1) axr.set_box_aspect(1 / 3) axr.yaxis.set_visible(False) axr.xaxis.set_ticks([0, np.pi, 2 * np.pi], ["0", r"$\pi$", r"$2\pi$"])
- 创建左右两个子图:
axl对应左侧单位圆画布,axr对应右侧正弦曲线画布 sharey=True:让两个子图共享Y轴范围(均为[-1,1]),保证视觉上的坐标对应关系width_ratios=[1,3]:设置左子图宽度为右子图的1/3,wspace=0.1缩小子图间距,让整体布局更紧凑axl.set_aspect(1):强制左子图为正方形,避免单位圆被拉伸变形axr.set_box_aspect(1/3):设置右子图宽高比为3:1,适配正弦曲线X轴0到2π的范围- 隐藏右子图Y轴,自定义X轴刻度为数学符号,让画面更简洁专业
3. 静态元素绘制
theta = np.linspace(0, 2 * np.pi, 50) axl.plot(np.cos(theta), np.sin(theta), "k", lw=1) point, = axl.plot(0, 0, "o", color='red') sine, = axr.plot(theta, np.sin(theta), color='blue') con = ConnectionPatch( (1, 0), (0, 0), "data", "data", axesA=axl, axesB=axr, color="C0", ls="dotted", ) fig.add_artist(con)
- 绘制单位圆:用50个均匀分布的角度点计算单位圆坐标,画出黑色细线的圆作为基础背景
- 初始化移动红点:
point, = ...的逗号用于获取plot返回的单个Line2D对象,后续动画会实时更新这个点的位置 - 初始化正弦曲线:同样获取
Line2D对象,后续动态更新它的绘制范围 - 创建跨子图虚线:初始连接左子图(1,0)(单位圆右顶点)和右子图(0,0),指定虚线样式后添加到画布,后续会随红点移动更新连接位置
4. 动画核心更新函数
def animate(i): x = np.linspace(0, i, int(i * 25 / np.pi)) sine.set_data(x, np.sin(x)) x, y = np.cos(i), np.sin(i) point.set_data([x], [y]) con.xy1 = x, y con.xy2 = i, y return point, sine, con
这是每帧动画的核心逻辑,参数i是当前帧对应的角度值(从0到2π):
- 更新正弦曲线:生成从0到当前角度
i的X轴数据,计算对应的正弦值,用set_data让曲线只绘制到当前角度,实现“逐步画曲线”的效果 - 更新红点位置:计算当前角度对应的单位圆坐标,更新红点的位置
- 更新连接虚线:把虚线的两端分别绑定到左侧红点和右侧正弦曲线上的对应点,保证二者始终联动
- 返回需要重绘的元素:告诉Matplotlib这三个元素在当前帧需要刷新
5. 动画配置与导出
fps = 60 duration = 5 total_frames = fps * duration frames = np.linspace(0, 2 * np.pi, total_frames) ani = animation.FuncAnimation( fig, animate, frames=frames, interval=1000/fps, blit=False, repeat=True, ) output_path = os.path.join(desktop_path, "animation_seno-2.mp4") Writer = animation.writers['ffmpeg'] writer = Writer(fps=fps, metadata=dict(artist='Lenin'), bitrate=1800) ani.save(output_path, writer=writer) plt.show()
- 帧率与总帧数:设置60fps、5秒时长,总共有300帧动画
- 角度序列:生成300个从0到2π的均匀角度值,每个角度对应一帧
- 创建动画对象:
FuncAnimation循环调用animate函数,每帧传入一个角度值;interval设置每帧间隔(约16.67ms),repeat=True让动画循环播放 - 导出动画:指定保存路径,用
ffmpeg作为编码器,设置帧率、作者元数据和比特率,最后保存为MP4文件 plt.show():弹出窗口显示动画
内容的提问来源于stack exchange,提问作者Lenin de Castilhos
相关产品推荐
相关产品推荐

