本文目录导读:

我来分享几种用Matplotlib制作动画的常用方法,从简单到复杂。
基础动画:FuncAnimation(最常用)
示例1:正弦波动画
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
# 创建图形
fig, ax = plt.subplots()
x = np.linspace(0, 2*np.pi, 100)
line, = ax.plot(x, np.sin(x), 'b-', linewidth=2)
# 设置坐标轴
ax.set_xlim(0, 2*np.pi)
ax.set_ylim(-1.5, 1.5)
ax.grid(True, alpha=0.3)
# 动画更新函数
def update(frame):
line.set_ydata(np.sin(x + frame/10)) # 更新y数据
ax.set_title(f'正弦波动画 - 帧: {frame}')
return line,
# 创建动画
ani = FuncAnimation(fig, update, frames=100, interval=50, blit=True)
# 显示动画
plt.show()
# 保存动画(可选)
# ani.save('sine_wave.gif', writer='pillow', fps=20)
示例2:散点图动态更新
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
# 准备数据
np.random.seed(42)
n_points = 50
x = np.random.randn(n_points)
y = np.random.randn(n_points)
colors = np.random.rand(n_points)
sizes = np.random.randint(50, 200, n_points)
fig, ax = plt.subplots(figsize=(8, 6))
scatter = ax.scatter(x, y, c=colors, s=sizes, alpha=0.6)
ax.set_xlim(-3, 3)
ax.set_ylim(-3, 3)
ax.grid(True, alpha=0.3)
ax.set_title('动态散点图')
def update(frame):
# 更新数据点位置
new_x = x + np.random.randn(n_points) * 0.1
new_y = y + np.random.randn(n_points) * 0.1
scatter.set_offsets(np.c_[new_x, new_y])
scatter.set_sizes(sizes + np.random.randint(-10, 10, n_points))
ax.set_title(f'动态散点图 - 帧: {frame}')
return scatter,
ani = FuncAnimation(fig, update, frames=100, interval=100, blit=True)
plt.show()
3D动画
示例3:3D曲面动画
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from mpl_toolkits.mplot3d import Axes3D
# 创建3D图形
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')
# 生成数据网格
x = np.linspace(-5, 5, 50)
y = np.linspace(-5, 5, 50)
X, Y = np.meshgrid(x, y)
# 初始Z值
Z = np.sin(np.sqrt(X**2 + Y**2))
# 绘制初始曲面
surf = ax.plot_surface(X, Y, Z, cmap='viridis', alpha=0.8)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
def update(frame):
ax.clear()
# 动态变化的曲面
Z = np.sin(np.sqrt(X**2 + Y**2) + frame/10) * (1 + 0.1*np.sin(frame/5))
# 重绘
surf = ax.plot_surface(X, Y, Z, cmap='viridis', alpha=0.8)
ax.set_xlim(-5, 5)
ax.set_ylim(-5, 5)
ax.set_zlim(-1.5, 1.5)
ax.set_title(f'3D曲面动画 - 帧: {frame}')
return surf,
ani = FuncAnimation(fig, update, frames=100, interval=50)
plt.show()
复杂案例:物理模拟
示例4:粒子系统动画
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
class Particle:
def __init__(self, x, y, vx, vy, color, size):
self.x = x
self.y = y
self.vx = vx
self.vy = vy
self.color = color
self.size = size
def update(self, dt=0.1):
# 简单物理模拟
self.x += self.vx * dt
self.y += self.vy * dt
# 边界碰撞
if abs(self.x) > 5:
self.vx *= -1
if abs(self.y) > 5:
self.vy *= -1
# 创建粒子
np.random.seed(42)
n_particles = 30
particles = []
for _ in range(n_particles):
particles.append(Particle(
x=np.random.uniform(-4, 4),
y=np.random.uniform(-4, 4),
vx=np.random.uniform(-2, 2),
vy=np.random.uniform(-2, 2),
color=np.random.rand(3),
size=np.random.uniform(20, 80)
))
# 创建图形
fig, ax = plt.subplots(figsize=(8, 8))
scatter = ax.scatter([], [], c=[], s=[])
ax.set_xlim(-5, 5)
ax.set_ylim(-5, 5)
ax.grid(True, alpha=0.3)
ax.set_aspect('equal')
def update(frame):
# 更新所有粒子位置
for p in particles:
p.update()
# 更新散点图数据
x_data = [p.x for p in particles]
y_data = [p.y for p in particles]
colors = [p.color for p in particles]
sizes = [p.size for p in particles]
scatter.set_offsets(np.c_[x_data, y_data])
scatter.set_color(colors)
scatter.set_sizes(sizes)
ax.set_title(f'粒子系统模拟 - 帧: {frame}')
return scatter,
ani = FuncAnimation(fig, update, frames=200, interval=50, blit=True)
plt.show()
高级技巧
示例5:多子图同步动画
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
# 创建多个子图
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
ax1, ax2, ax3, ax4 = axes.flatten()
# 初始化数据
t = np.linspace(0, 2*np.pi, 100)
line1, = ax1.plot(t, np.sin(t), 'r-', linewidth=2)
line2, = ax2.plot(t, np.cos(t), 'b-', linewidth=2)
line3, = ax3.plot(t, np.sin(t), 'g-', linewidth=2)
scatter = ax4.scatter([], [], c=[], s=[])
# 设置坐标轴
for ax in [ax1, ax2, ax3, ax4]:
ax.set_xlim(0, 2*np.pi)
ax.set_ylim(-1.5, 1.5)
ax.grid(True, alpha=0.3)
ax4.set_title('散点图')
ax1.set_title('正弦波')
ax2.set_title('余弦波')
ax3.set_title('正弦波')
def update(frame):
phase = frame / 20
# 更新线条
line1.set_ydata(np.sin(t + phase))
line2.set_ydata(np.cos(t + phase))
line3.set_ydata(np.sin(t + 2*phase))
# 更新散点图
x_data = np.cos(t + phase)
y_data = np.sin(t + phase)
scatter.set_offsets(np.c_[x_data[::5], y_data[::5]])
scatter.set_color(np.random.rand(20, 3))
return line1, line2, line3, scatter,
ani = FuncAnimation(fig, update, frames=200, interval=30, blit=True)
plt.tight_layout()
plt.show()
实用技巧总结
保存动画
# 保存为GIF
ani.save('animation.gif', writer='pillow', fps=20)
# 保存为MP4(需要ffmpeg)
ani.save('animation.mp4', writer='ffmpeg', fps=20)
# 保存为HTML5视频(在Jupyter中)
from IPython.display import HTML
HTML(ani.to_html5_video())
性能优化
# 1. 使用blit=True减少重绘 ani = FuncAnimation(fig, update, frames=100, interval=50, blit=True) # 2. 减少数据点数量 x = np.linspace(0, 2*np.pi, 50) # 而不是1000 # 3. 使用更简单的图形元素 # 首选:Line2D, Scatter # 避免:填充区域、等高线
常见问题解决
# 1. 动画不显示 plt.ion() # 打开交互模式 # 2. 内存泄露 ani.event_source.stop() # 停止动画 # 3. 调整帧率 # interval=50 表示50毫秒一帧,即20fps ani = FuncAnimation(fig, update, frames=100, interval=50)
这些案例覆盖了Matplotlib动画的主要应用场景,开始学习时推荐从FuncAnimation和简单的一维动画入手,逐步扩展到复杂应用。