首页 / 文档中心 / 进阶

Manim 动画编排:AnimationGroup、LaggedStart、Succession 与节奏设计

发布于 2026-11-02 · 进阶系列 · 约 4 分钟读完 · 中文正文 + English TL;DR
▶ 渲染效果实录(manim 渲染输出,代码见正文,可直接复现)
English TL;DR

Good animations are choreographed, not just played. This chapter is Manim's timing layer. Multiple animations in one play() run simultaneously — AnimationGroup is the same thing as an object, with a lag_ratio knob that staggers start times (lag_ratio=0 fully simultaneous, 1 strictly sequential). LaggedStart is sugar for lag_ratio around 0.1-0.3, the "wave" feel of 3b1b grids; LaggedStartMap applies one animation across a list with stagger. Succession chains animations where each begins when the previous finishes, useful when objects must appear in causal order. rate_func shapes progress within run_time: smooth (default ease-in-out), linear for motion that should feel mechanical, there_and_back for emphasis pulses, rush_into/rush_from for snappy entrances, wiggle for attention. Recipes include count-up numbers, staggered grid reveals, a wave of dots, and the classic 'title drops then content follows' sequencing.

编排三原语

from manim import *

# 1) 同时播放: play() 里放多个动画
self.play(Create(circle), Write(text), run_time=2)

# 2) 错峰播放: LaggedStart, 第二个动画等一会儿再开始
self.play(LaggedStart(FadeIn(a), FadeIn(b), FadeIn(c),
                      lag_ratio=0.25))

# 3) 严格串行: Succession, 上一个播完才轮到下一个
self.play(Succession(GrowFromCenter(a), TransformFromCopy(a, b)))

一句话区分:LaggedStart 是"重叠的先后"(波浪感),Succession 是"不重叠的先后"(因果感)。

AnimationGroup 与 lag_ratio

AnimationGroup 是更底层的构造,lag_ratio 精确控制错峰程度:

anims = [GrowFromCenter(d) for d in dots]

self.play(AnimationGroup(*anims, lag_ratio=0))    # 完全同时
self.play(AnimationGroup(*anims, lag_ratio=0.3))  # 波浪
self.play(AnimationGroup(*anims, lag_ratio=1))    # 等价串行

lag_ratio 的含义:每个动画的开始时间 = lag_ratio × 前一个动画的时长,依次顺延。0.15~0.3 是 3b1b 网格动画最常用的"呼吸感"区间。

LaggedStartMap:一行搞定列表错峰

titles = VGroup(*[Text(t, font_size=30) for t in
                  ["问题", "建模", "求解", "验证"]])
titles.arrange(DOWN, buff=0.6).to_edge(LEFT)

self.play(LaggedStartMap(FadeIn, titles, shift=RIGHT * 0.5,
                         lag_ratio=0.3))

LaggedStartMap(动画类, 对象列表) 自动把同一个动画应用到列表每个成员并错峰,比手写推导简洁得多。

rate_func:单段动画的缓动曲线

函数手感场景
smooth(默认)缓入缓出绝大多数动画
linear匀速物理、函数扫描
there_and_back去了又回强调脉冲、闪一下
rush_into急进缓出快速入场
rush_from缓入急出快速离场
wiggle抖动吸引注意力
self.play(square.animate.scale(1.2), rate_func=there_and_back)
# 放大又缩回原样, 纯强调, 不留痕迹

数字滚动:编排 + updater 的经典组合

num = ValueTracker(0)
label = Integer(0).scale(1.6)
label.add_updater(lambda m: m.set_value(num.get_value()))
self.add(label)
self.play(num.animate.set_value(2026), run_time=2,
          rate_func=rush_from)   # 先慢后快, 收尾干脆

节奏设计四原则

  1. 一段 play 一个意图:一次播放只讲一件事(进场/移动/强调),观众才跟得上。
  2. 错峰 > 同时:列表内容几乎永远用 LaggedStart,同时弹出显得廉价。
  3. 留白即节拍:self.wait(0.5) 的短停顿比连续动画更有节奏感。
  4. 入场快、退场更快:入场 run_time 1~1.5s,退场 0.5~0.8s,信息密度不塌。

综合示例:章节开场

class ChapterOpen(Scene):
    def construct(self):
        num = Text("02", font_size=96, weight=BOLD, color=GREY_D)
        title = Text("函数的变换", font="Microsoft YaHei", font_size=52)
        rule = Line(LEFT * 4, RIGHT * 4).next_to(title, DOWN, buff=0.4)
        grp = VGroup(num, title, rule).arrange(DOWN, buff=0.5)

        self.play(FadeIn(num, shift=DOWN * 0.5), run_time=0.8)
        self.play(Write(title), Create(rule), run_time=1.2)
        self.play(grp.animate.to_edge(UP).scale(0.7), run_time=0.8)
        self.wait()

三个 play、三种意图(入场 → 标题成型 → 移位收拢),这就是"有节奏"的最小样例。