创建家族

创建(Creation)动画管"物体如何进场":描边、填色、逐字、逐批、打字机……它们的共同点是 introducer=True——动画开始时物体进入场景。先给一张选型表,再逐个展开。

需求

用哪个

图形/曲线一笔画出

Create

先描边再填充的厚重感

DrawBorderThenFill

文字沿笔画书写

Write

子对象一批批/一个个出现

ShowIncreasingSubsets / ShowSubmobjectsOneByOne

文字逐字符/逐词淡入

AddTextLetterByLetter / AddTextWordByWord

打字机效果(带光标)

TypeWithCursor

成群物体螺旋飞入

SpiralIn

Create

Create(mobject, lag_ratio=1.0, introducer=True, **kwargs) 官方文档 ↗

Create(mobject, lag_ratio=1.0) 沿 VMobject 的描边逐点"画"出物体,是图形进场的主力。lag_ratio=1.0 让组成它的各条子路径依次绘制而非同时。

digraph G { graph [rankdir=LR, bgcolor="transparent", nodesep="0.25", ranksep="0.35"]; node [shape=box, style="rounded,filled", fontname="Helvetica,sans-serif", fontsize=11, height=0.3, color="#8b93a1", fillcolor="#f2f4f7", fontcolor="#1f2733"]; edge [color="#a6adba", arrowsize=0.6, penwidth=1.1]; "Create" -> "DrawBorderThenFill"; "DrawBorderThenFill" -> "Animation"; "Animation" -> "object"; }

Uncreate

Uncreate(mobject, reverse_rate_function=True, remover=True, **kwargs) 官方文档 ↗

Uncreate 是 Create 的镜像:reverse_rate_function=True 反转进度、remover=True 播完把物体移出场景——相当于沿原路径"擦除"。

三个图形分别用 Create、DrawBorderThenFill 进场;随后 Uncreate 把前两个擦除。

查看源码 creation_family.py
"""CreationFamily: Create / DrawBorderThenFill / Uncreate."""

from manim import *


class CreationFamily(Scene):
    def construct(self):
        shapes = VGroup(
            Square().set_fill(RED, opacity=0.5),
            Circle().set_fill(GREEN, opacity=0.5),
            Triangle().set_fill(BLUE, opacity=0.5),
        ).arrange(RIGHT, buff=1.2)

        self.play(Create(shapes[0]), run_time=0.9)
        self.play(DrawBorderThenFill(shapes[1]), run_time=1.2)
        self.add(shapes[2])
        self.wait(0.3)
        self.play(Uncreate(shapes[0]), run_time=0.8)
        self.play(Uncreate(shapes[1]), run_time=0.8)
        self.wait(0.3)

Write

Write(vmobject, rate_func=<function linear at 0…, reverse=False, **kwargs) 官方文档 ↗

Write(vmobject, rate_func=linear, reverse=False) 让 VMobject(常用于 Text/MathTex)沿笔画描出轮廓再填充。注意它的默认 rate_func 是 linear 而不是大多数动画的 smooth,书写节奏更均匀。

digraph G { graph [rankdir=LR, bgcolor="transparent", nodesep="0.25", ranksep="0.35"]; node [shape=box, style="rounded,filled", fontname="Helvetica,sans-serif", fontsize=11, height=0.3, color="#8b93a1", fillcolor="#f2f4f7", fontcolor="#1f2733"]; edge [color="#a6adba", arrowsize=0.6, penwidth=1.1]; "Write" -> "Draw"; "Draw" -> "DrawBorderThenFill"; "DrawBorderThenFill" -> "Animation"; "Animation" -> "object"; }

Unwrite

Unwrite(vmobject, rate_func=<function linear at 0…, reverse=True, **kwargs) 官方文档 ↗

Unwrite(vmobject, rate_func=linear, reverse=True) 是 Write 的逆过程:按 reverse=True 从尾到头擦除,remover=True 收场。英文教材中 Write/Unwrite 是演示公式推导的经典组合。

DrawBorderThenFill

DrawBorderThenFill(vmobject, run_time=2, rate_func=<function double_smoo…, stroke_width=2, stroke_color=None, introducer=True, **kwargs) 官方文档 ↗

DrawBorderThenFill(vmobject, run_time=2, rate_func=double_smooth, stroke_width=2, stroke_color=None) 分两段:先用细线描边,再展开填充。默认 run_time=2 比多数动画长,自带"隆重登场"的节奏;stroke_width/stroke_color 控制前一段描边的粗细与颜色(默认取物体描边色)。

ShowIncreasingSubsets

ShowIncreasingSubsets(group, suspend_mobject_updating=False, int_func=<ufunc 'floor'>, reverse_rate_function=False, **kwargs) 官方文档 ↗

ShowIncreasingSubsets(group, suspend_mobject_updating=False, int_func=floor) 让 VGroup 的子对象一批一批出现:进度 α 对应前 int_func(α × n) 个子对象可见。int_func 决定取整方式(默认 floor,即 α=0.5、n=5 时显示前 2 个)。

ShowSubmobjectsOneByOne

ShowSubmobjectsOneByOne(group, int_func=<ufunc 'ceil'>, **kwargs) 官方文档 ↗

ShowSubmobjectsOneByOne(group, int_func=ceil) 与前者类似,但节奏是"逐个点亮再熄灭":每个子对象依次短暂出现后消失,动画结束只保留最后一个——适合做扫描、点名式的强调。

圆点用 ShowIncreasingSubsets 一批批入场;正方形用 ShowSubmobjectsOneByOne 逐个点亮;最后六个圆环 SpiralIn 螺旋飞入。

查看源码 creation_subsets.py
"""CreationSubsets: ShowIncreasingSubsets / ShowSubmobjectsOneByOne / SpiralIn."""

from manim import *


class CreationSubsets(Scene):
    def construct(self):
        dots = VGroup(*[Dot().shift(i * RIGHT * 0.9) for i in range(5)])
        dots.shift(UP * 1.5 + LEFT * 1.8)

        # 子对象一批一批地显示出来
        self.play(ShowIncreasingSubsets(dots), run_time=1.5)
        self.wait(0.3)

        # 子对象逐个显示(最后只保留最后一个?不——逐个点亮)
        squares = VGroup(*[Square(0.5).shift(i * RIGHT * 0.9) for i in range(4)])
        squares.shift(DOWN * 0.5 + LEFT * 1.4)
        self.play(ShowSubmobjectsOneByOne(squares), run_time=1.6)
        self.wait(0.2)

        # 螺旋飞入
        rings = VGroup(*[Circle(radius=0.25).shift(1.5 * np.cos(i) * RIGHT + 1.5 * np.sin(i) * UP) for i in np.linspace(0, 2 * PI, 6)[:-1]])
        self.play(SpiralIn(rings), run_time=1.4)
        self.wait(0.3)

AddTextLetterByLetter

AddTextLetterByLetter(text, suspend_mobject_updating=False, int_func=<ufunc 'ceil'>, rate_func=<function linear at 0…, time_per_char=0.1, run_time=None, reverse_rate_function=False, introducer=True, **kwargs) 官方文档 ↗

AddTextLetterByLetter(text, rate_func=linear, time_per_char=0.1, run_time=None) 让 Text 的字符逐个淡入。每个字符耗时 time_per_char 秒;不传 run_time 时总时长自动算为 time_per_char × 字符数(下限 0.1 秒)。

RemoveTextLetterByLetter

RemoveTextLetterByLetter(text, suspend_mobject_updating=False, int_func=<ufunc 'ceil'>, rate_func=<function linear at 0…, time_per_char=0.1, run_time=None, reverse_rate_function=True, introducer=False, remover=True, **kwargs) 官方文档 ↗

RemoveTextLetterByLetter 是它的逆动画:reverse_rate_function=True 从后往前逐字符淡出,remover=True 播完移除。

AddTextWordByWord

AddTextWordByWord(text_mobject, run_time=None, time_per_char=0.06, **kwargs) 官方文档 ↗

AddTextWordByWord(text_mobject, time_per_char=0.06) 按空格把文字切成词,逐词整体淡入。time_per_char 控制每个词内部的节奏。

提示

中文没有天然空格:AddTextWordByWord(Text("一词 一词 出现")) 依赖你手动插入的空格分词;不分词的中文整句会作为一个词整体出现。纯中文逐字进场请用 AddTextLetterByLetter。

TypeWithCursor

TypeWithCursor(text, cursor, buff=0.1, keep_cursor_y=True, leave_cursor_on=True, time_per_char=0.1, reverse_rate_function=False, introducer=True, **kwargs) 官方文档 ↗

TypeWithCursor(text, cursor, buff=0.1, keep_cursor_y=True, leave_cursor_on=True, time_per_char=0.1) 在逐字符出现的同时,把一个光标物体贴在已出现文字的末尾,形成打字机效果。cursor 是任意的 Mobject(常用细长 Rectangle);keep_cursor_y=True 让光标始终与文字中线对齐;leave_cursor_on=True 播完后保留光标。

UntypeWithCursor

UntypeWithCursor(text, cursor=None, time_per_char=0.1, reverse_rate_function=True, introducer=False, remover=True, **kwargs) 官方文档 ↗

UntypeWithCursor(text, cursor=None, ...) 是逆过程(打字删除),remover=True 播完把文字移出场景。

常见错误

UntypeWithCursor 的 cursor 形参默认是 None,但实际上必须显式传入:传 None 会在动画开始阶段崩溃(本机 v0.21.0 实测抛 AttributeError: 'NoneType' object has no attribute 'get_y')。建议把 TypeWithCursor 用过的同一个光标再传给它。

TypeWithCursor 带着矩形光标逐字打出文字;UntypeWithCursor 用同一光标把文字逐个删掉。

查看源码 creation_typing.py
"""CreationTyping: TypeWithCursor / UntypeWithCursor."""

from manim import *


class CreationTyping(Scene):
    def construct(self):
        text = Text("typewriter", font_size=44)
        cursor = Rectangle(
            height=0.55, width=0.32, stroke_width=2
        ).set_fill(opacity=0)

        # 光标贴着文字打字
        self.play(TypeWithCursor(text, cursor), run_time=2.0)
        self.wait(0.3)
        # UntypeWithCursor 的 cursor 默认是 None,不显式传会在 begin() 崩溃
        self.play(UntypeWithCursor(text, cursor), run_time=1.5)
        self.wait(0.3)

提示

这两个动画只支持 Text,不支持 MathTex(官方文档明确说明)。

SpiralIn

SpiralIn(shapes, scale_factor=8, fade_in_fraction=0.3, **kwargs) 官方文档 ↗

SpiralIn(shapes, scale_factor=8, fade_in_fraction=0.3) 让一组物体从远处(scale_factor 倍)螺旋收拢飞入,最后 fade_in_fraction 比例的行程内淡入收尾,适合表现"一群东西聚拢成形"。

ShowPartial

ShowPartial(mobject, **kwargs) 官方文档 ↗

ShowPartial 是 Create 系动画的抽象基类:按 VMobject 已有的部分绘制进度(0 到 1)显示物体,自己不规定进度如何推进。日常几乎不会直接实例化它,但自定义"按自定义曲线进度显示"的动画时可以继承。

digraph G { graph [rankdir=LR, bgcolor="transparent", nodesep="0.25", ranksep="0.35"]; node [shape=box, style="rounded,filled", fontname="Helvetica,sans-serif", fontsize=11, height=0.3, color="#8b93a1", fillcolor="#f2f4f7", fontcolor="#1f2733"]; edge [color="#a6adba", arrowsize=0.6, penwidth=1.1]; "ShowPartial" -> "Animation"; "Animation" -> "object"; }

自测

✏️ 练习

一行 Text("Hello World") 想做出"逐词淡入"的效果,用哪个动画?对 "你好世界"(无空格)会得到什么效果?

✅ 参考答案

用 AddTextWordByWord:它按空格切词,Hello 和 World 会依次整体淡入。"你好世界" 没有空格,整句会被当作一个词一次性出现;中文逐字效果应改用 AddTextLetterByLetter。

✏️ 练习

ShowIncreasingSubsets 与 ShowSubmobjectsOneByOne 播完后画面有什么区别?

✅ 参考答案

ShowIncreasingSubsets:子对象只增不减,播完时全部保留;ShowSubmobjectsOneByOne:每个子对象闪亮后消失,播完时只剩最后一个。

下一步

创建家族管"从无到有",下一节是另一组进场/离场动画:从一点"长出来"的 Grow 家族,与淡入淡出的 Fade 家族。