如何用ArtistAnimation实现不同帧数量子图的同总时长动画?
解决ArtistAnimation实现不同帧数子图同总时长播放的问题
这个问题确实戳中了ArtistAnimation的一个小局限——它的interval参数是全局统一的,没法给不同子图单独设置播放间隔。不过既然你倾向用ArtistAnimation实现,我们可以通过扩展短帧序列的长度来达成相同总时长内播完不同帧数的需求,思路其实很简单:让帧数少的子图重复显示部分帧,使得每个原始帧的显示时长刚好等于总时长除以它的原始帧数,这样两个子图就能同时走完各自的所有帧。
具体实现步骤
- 分别生成两个子图的原始帧序列:先为每个子图单独生成对应的帧,不用考虑同步问题;
- 确定总目标时长:比如我们设定总时长为10秒(10000ms);
- 扩展短帧序列到长序列的长度:通过均匀重复短序列的帧,让两个序列的总帧数一致;
- 构建统一的帧列表:将两个子图的对应帧(包括重复的帧)组合成ArtistAnimation需要的格式;
- 计算全局interval:用总时长除以总帧数,保证总时长符合预期。
修改后的完整代码
import numpy as np import itertools import matplotlib.pyplot as plt import matplotlib.mlab as ml import matplotlib.animation as animation def f(x,y,a): return ((x/a)**2+y**2) # 两个子图使用不同长度的参数序列(不同帧数) avals1 = np.linspace(0.1,1,10) # 子图1:10帧 avals2 = np.linspace(0.1,1,5) # 子图2:5帧 total_duration = 10000 # 总时长:10秒(10000毫秒) xaxis = np.linspace(-2,2,9) yaxis = np.linspace(-2,2,9) xy = itertools.product(xaxis,yaxis) xy = list(map(list,xy)) xy = np.array(xy) x = xy[:,0] y = xy[:,1] fig, [ax1,ax2] = plt.subplots(2) # 生成子图1的原始帧 frames1 = [] for a in avals1: xi = np.linspace(min(x), max(x), len(x)) yi = np.linspace(min(y), max(y), len(y)) zi = ml.griddata(x, y, f(x, y, a), xi, yi, interp='linear') im = ax1.imshow(zi, animated=True, vmin=0, vmax=400) frames1.append(im) # 生成子图2的原始帧 frames2 = [] for a in avals2: xi = np.linspace(min(x), max(x), len(x)) yi = np.linspace(min(y), max(y), len(y)) zi = ml.griddata(x, y, f(x, y, a), xi, yi, interp='linear') im = ax2.imshow(zi, animated=True, vmin=0, vmax=400) frames2.append(im) # 扩展短帧序列到长序列的长度,均匀重复原始帧 def extend_frames(frames, target_length): if len(frames) == target_length: return frames extended = [] # 计算每个原始帧需要覆盖的目标帧数 step = target_length / len(frames) for idx in range(target_length): # 找到对应原始帧的索引 frame_idx = int(idx // step) extended.append(frames[frame_idx]) return extended max_frame_count = max(len(frames1), len(frames2)) extended_frames1 = extend_frames(frames1, max_frame_count) extended_frames2 = extend_frames(frames2, max_frame_count) # 构建ArtistAnimation需要的帧列表 ims = [] for i in range(max_frame_count): # 获取当前帧对应的参数值,用于标题显示 a1 = avals1[int(i // (max_frame_count / len(avals1)))] a2 = avals2[int(i // (max_frame_count / len(avals2)))] title = plt.text( 0.5, 1.02, f"子图1参数a={a1:.2f} | 子图2参数a={a2:.2f}", ha='center', transform=fig.transFigure ) ims.append([extended_frames1[i], extended_frames2[i], title]) # 计算全局interval,保证总时长符合设定 interval = total_duration // max_frame_count ani = animation.ArtistAnimation(fig, ims, interval=interval, blit=False) plt.show()
原理说明
在这个方案里,子图2只有5帧,我们把它的帧序列扩展到10帧(和子图1一致),每个原始帧重复显示2次。全局interval设为1000ms(10000ms / 10帧),这样子图2的每个原始帧会显示2秒(2*1000ms),子图1的每个帧显示1秒,最终两个子图都能在10秒内播完各自的所有原始帧,完美实现同总时长的需求。
内容的提问来源于stack exchange,提问作者user32882
相关产品推荐
相关产品推荐

