Transform 家族

Transform 是 Manim 的灵魂:让物体 A 逐步"变成"物体 B。本节覆盖三个常用变换三兄弟、两组状态记忆(MoveToTarget/Restore)、一组方法动画(Apply*)、位置交换与渐隐变换。

Transform

Transform(mobject, target_mobject=None, path_func=None, path_arc=0, path_arc_axis=array([0., 0., 1.]), path_arc_centers=None, replace_mobject_with_target_in_scene=False, **kwargs) 官方文档 ↗

Transform(mobject, target_mobject, ...) 把物体原地变形为目标的样子:结束后场景里是原物体(已变成目标的形态),目标物体本身并不加入场景。

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]; "Transform" -> "Animation"; "Animation" -> "object"; }

name

type

default

desc

target_mobject

Mobject | None

None

要变成的目标

path_func

Callable | None

None

每个点的运动路线函数;None 时由 path_arc 决定

path_arc

float

0

路线为圆弧时的弧度;非 0 时各点沿圆弧走

path_arc_axis

np.ndarray

OUT

圆弧轴向

path_arc_centers

Point3DLike 等 | None

None

各点分别绕指定圆心转弧

replace_mobject_with_target_in_scene

bool

False

True 时结束后用目标物替换原物入场景

path_func 决定点从起点到终点"走哪条路",常用取值是 manim.utils.paths 里的路径函数(straight_path、path_along_arc、clockwise_path 等),它们在"运动变形"一节有完整表格。改变路线不改变起止状态:绕多大的弧,终点都精确落在目标态。

ReplacementTransform

ReplacementTransform(mobject, target_mobject, **kwargs) 官方文档 ↗

ReplacementTransform(mobject, target_mobject):场景效果与 Transform 几乎一致,区别在于结束后场景里保留的是目标物体(原物体被移除)。想继续操作变换结果时用更顺手——拿到的就是 target_mobject 本身。

TransformFromCopy

TransformFromCopy(mobject, target_mobject, **kwargs) 官方文档 ↗

TransformFromCopy(mobject, target_mobject):以 mobject 为模板复制出一份变成目标,原物体原封不动留在场景。等于 Transform + "保留原件",常用于"由已知推出新结论"的推导演示。

类

结束后场景里是谁

原物体

典型场景

Transform

原物体(已变形)

变形为目标

一般变形

ReplacementTransform

目标物体

被替换移除

要继续操作结果

TransformFromCopy

原物体 + 新生成的目标

保留

推导、复制出新对象

正方形 Transform 成圆、ReplacementTransform 成三角形、TransformFromCopy 复制出第二个圆;随后 MoveToTarget 就位与 Restore 还原。

查看源码 transform_basics.py
"""TransformBasics: Transform / ReplacementTransform / TransformFromCopy / MoveToTarget / Restore."""

from manim import *


class TransformBasics(Scene):
    def construct(self):
        square = Square().set_fill(RED, opacity=0.5)
        circle = Circle().set_fill(GREEN, opacity=0.5)
        triangle = Triangle().set_fill(BLUE, opacity=0.5)

        # Transform:原地变形,原物体留在场景里(变成目标的样子)
        self.add(square)
        self.play(Transform(square, circle), run_time=0.6)
        self.wait(0.2)

        # ReplacementTransform:目标物接替原物出现在场景中
        self.play(ReplacementTransform(square, triangle), run_time=0.6)
        self.wait(0.2)

        # TransformFromCopy:从原物“复制一份”变过去,原物保留
        self.play(TransformFromCopy(triangle, circle.copy().shift(RIGHT * 2)), run_time=0.6)
        self.wait(0.2)

        # generate_target + MoveToTarget:先准备好目标状态,再一次变换到位
        triangle.generate_target()
        triangle.target.shift(LEFT * 2).scale(0.5)
        self.play(MoveToTarget(triangle), run_time=0.6)
        self.wait(0.2)

        # save_state + Restore:记住当前状态,之后一键还原
        triangle.save_state()
        self.play(triangle.animate.rotate(PI / 2).shift(UP * 0.5), run_time=0.5)
        self.play(Restore(triangle), run_time=0.5)
        self.wait(0.3)

ClockwiseTransform

ClockwiseTransform(mobject, target_mobject, path_arc=-3.141592653589793, **kwargs) 官方文档 ↗

ClockwiseTransform(mobject, target_mobject, path_arc=-PI):Transform 的变体,各点沿顺时针半圆弧到达目标(默认 path_arc=-PI,可改弧度)。

CounterclockwiseTransform

CounterclockwiseTransform(mobject, target_mobject, path_arc=3.141592653589793, **kwargs) 官方文档 ↗

CounterclockwiseTransform(mobject, target_mobject, path_arc=PI):同上,方向为逆时针。两者常用于强调"翻过去"的变换感,也可通过 Transform(..., path_arc=...) 等效实现。

MoveToTarget

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

MoveToTarget(mobject) 把物体变换到它的 target 属性所记录的状态。target 不会自动出现,必须先调用:

circle.generate_target()        # 生成一份副本存到 circle.target
circle.target.shift(RIGHT * 2).scale(0.5)  # 在这份副本上布置终态
self.play(MoveToTarget(circle))

常见错误

忘记 generate_target() 就直接 MoveToTarget(m) 会在渲染时崩溃(本机 v0.21.0 实测报 NotImplementedError: get_point_mobject not implemented for Mobject;官方 docstring 声明的校验是 ValueError)。养成"先 generate_target、改 target、再播放"的三步习惯。

Restore

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

Restore(mobject) 把物体还原到 save_state() 时记录的状态:

square.save_state()
self.play(square.animate.shift(RIGHT * 2))   # 随便折腾
self.play(Restore(square))                   # 一键还原

save_state() 把当前状态副本存进 saved_state 属性,可反复 Restore。

机制

记录什么

怎么改终态

还原/变换

generate_target + MoveToTarget

一份可编辑的目标副本

直接改 m.target 的属性

只去不回

save_state + Restore

当前状态的快照

不需要(快照即终态)

去了还能回

ApplyMethod

ApplyMethod(method, *args, **kwargs) 官方文档 ↗

ApplyMethod(method, *args, **kwargs) 以"方法 + 参数"的形式播放任意 mobject 方法动画,等价于 .animate 的前身写法:

self.play(ApplyMethod(square.shift, LEFT * 2, run_time=1))
# 等价于
self.play(square.animate.shift(LEFT * 2), run_time=1)

新方法优先用 .animate;ApplyMethod 适合方法名在运行时才确定的场景。

ApplyFunction

ApplyFunction(function, mobject, **kwargs) 官方文档 ↗

ApplyFunction(function, mobject) 把 function(m) 的返回值作为终态做平滑变换——适合"目标状态不方便用链式方法表达"的自定义形变,如 lambda m: m.scale(1.5).set_color(YELLOW)。

ApplyMatrix

ApplyMatrix(matrix, mobject, about_point=array([0., 0., 0.]), **kwargs) 官方文档 ↗

ApplyMatrix(matrix, mobject, about_point=ORIGIN) 让每个点左乘 matrix 到达新位置,默认绕原点。旋转 90° 就是 np.array([[0,-1,0],[1,0,0],[0,0,1]])。

ApplyComplexFunction

ApplyComplexFunction(function, mobject, **kwargs) 官方文档 ↗

ApplyComplexFunction(function, mobject):ApplyFunction 的复数特化,function(z: complex) -> complex 把每个点当作复平面上的数做共形映射(如 lambda z: z**2),保角不变形好看。

ApplyPointwiseFunction

ApplyPointwiseFunction(function, mobject, run_time=3.0, **kwargs) 官方文档 ↗

ApplyPointwiseFunction(function, mobject, run_time=3.0):对每个点独立施加 function(point) -> point。注意默认 run_time=3.0 明显偏长,且逐点独立映射可能撕裂轮廓,复杂形变可考虑 Homotopy(见"运动变形"一节)。

ApplyPointwiseFunctionToCenter

ApplyPointwiseFunctionToCenter(function, mobject, **kwargs) 官方文档 ↗

ApplyPointwiseFunctionToCenter(function, mobject):只把 function 施加到物体中心,物体整体平移过去、内部形状保持不变——相当于"由中心决定的自定义 shift"。

ApplyMethod 播方法、ApplyFunction 自定义终态、ApplyMatrix 旋转 90°;最后 Swap 两物换位、CyclicReplace 三物循环换。

查看源码 transform_path_func.py
"""TransformPathFunc: ApplyMethod / ApplyFunction / ApplyMatrix / CyclicReplace / Swap."""

from manim import *


class TransformPathFunc(Scene):
    def construct(self):
        square = Square().set_fill(RED, opacity=0.5).shift(UP * 1.5)
        dot = Dot().shift(LEFT * 3 + DOWN * 1.5)
        tri = Triangle().set_fill(GREEN, opacity=0.5).shift(RIGHT * 3 + DOWN * 1.5)
        self.add(square, dot, tri)

        # ApplyMethod:以“方法名 + 参数”的形式播放方法动画
        self.play(ApplyMethod(square.shift, DOWN * 0.8), run_time=0.5)
        self.wait(0.2)

        # ApplyFunction:对物体每个点施加同一个函数
        self.play(
            ApplyFunction(lambda m: m.scale(1.6).set_color(YELLOW), dot),
            run_time=0.5,
        )
        self.wait(0.2)

        # ApplyMatrix:左乘一个矩阵(这里是 90° 旋转矩阵)
        rot = np.array([[0, -1, 0], [1, 0, 0], [0, 0, 1]])
        self.play(ApplyMatrix(rot, tri), run_time=0.6)
        self.wait(0.2)

        # Swap / CyclicReplace:多个物体沿弧线交换位置
        a = Circle(radius=0.3).set_fill(TEAL, opacity=0.6).shift(LEFT * 1.2 + DOWN * 0.5)
        b = Square(0.6).set_fill(PURPLE, opacity=0.6).shift(RIGHT * 1.2 + DOWN * 0.5)
        self.add(a, b)
        self.play(Swap(a, b), run_time=0.7)
        self.wait(0.2)

        c = Star().set_fill(GOLD, opacity=0.6).shift(RIGHT * 1.2 + UP * 1.2)
        self.add(c)
        self.play(CyclicReplace(a, b, c), run_time=0.8)
        self.wait(0.3)

CyclicReplace

CyclicReplace(*mobjects, path_arc=1.5707963267948966, **kwargs) 官方文档 ↗

CyclicReplace(*mobjects, path_arc=PI/2) 让多个物体沿圆弧循环交换位置:A→B 的位置、B→C 的位置、……最后一只回到 A 的位置(默认各绕 90° 弧)。

Swap

Swap(*mobjects, path_arc=1.5707963267948966, **kwargs) 官方文档 ↗

Swap(*mobjects, path_arc=PI/2) 是 CyclicReplace 的别名同族,语义相同——多只物体沿弧轮换位置。两只物体时就是最常见的"对调"。

ScaleInPlace

ScaleInPlace(mobject, scale_factor, **kwargs) 官方文档 ↗

ScaleInPlace(mobject, scale_factor) 绕物体自身中心缩放,等价于 m.animate.scale(factor) 但可嵌入组合动画。

ShrinkToCenter

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

ShrinkToCenter(mobject) 缩向中心的离场动画,等价于"以中心为基准缩到 0"的 ScaleInPlace(m, 0)。

TransformAnimations

TransformAnimations(start_anim, end_anim, rate_func=<function squish_rate…, **kwargs) 官方文档 ↗

TransformAnimations(start_anim, end_anim, rate_func=squish_rate_func(smooth, 0.25, 1)) 把一段动画变成另一段动画:不是变形物体,而是把两个动画本身当作变换两端(常用于开场镜头转换)。默认速率函数会把前 25% 的时间压掉,让衔接更利落。

FadeTransform

FadeTransform(mobject, target_mobject, stretch=True, dim_to_match=1, **kwargs) 官方文档 ↗

FadeTransform(mobject, target_mobject, stretch=True, dim_to_match=1):与 Transform 的"原地变形"不同,它是先淡出原物、再淡入目标物的交叉过渡,两个状态差异很大时(例如形状、子结构完全不同)比 Transform 更自然。stretch 控制是否拉伸对齐尺寸,dim_to_match 指定按哪个维度对齐。

提示

FadeTransform/FadeTransformPieces/FadeToColor 都在 manim.animation.transform 模块,不在 fading 模块——按名字找源码时容易找错地方。

FadeTransformPieces

FadeTransformPieces(mobject, target_mobject, stretch=True, dim_to_match=1, **kwargs) 官方文档 ↗

FadeTransformPieces(mobject, target_mobject, ...):按子对象一一对应做淡入淡出变换(内部用 FadeTransform 配对),VGroup 结构相似但成员不同时比整体 Transform 效果更好。

FadeToColor

FadeToColor(mobject, color, **kwargs) 官方文档 ↗

FadeToColor(mobject, color) 把物体颜色平滑过渡到目标色的便捷动画(Transform 到"同色副本"的封装)。

FadeTransform 让方块交叉过渡成圆;FadeTransformPieces 让四个方块逐个变成圆;最后 FadeToColor 整体换色。

查看源码 fade_transform.py
"""FadeTransformDemo: FadeTransform / FadeTransformPieces / FadeToColor."""

from manim import *


class FadeTransformDemo(Scene):
    def construct(self):
        # FadeTransform:先淡出原物、再淡入目标物(注意它在 transform 模块,不在 fading 模块)
        square = Square().set_fill(RED, opacity=0.6)
        circle = Circle().set_fill(GREEN, opacity=0.6)
        self.add(square)
        self.play(FadeTransform(square, circle), run_time=0.8)
        self.wait(0.2)

        # FadeTransformPieces:按子对象一一对应做淡入淡出变换
        src = VGroup(*[Square(0.4).set_fill(BLUE, opacity=0.6) for _ in range(4)]).arrange(RIGHT)
        dst = VGroup(*[Circle(0.25).set_fill(YELLOW, opacity=0.6) for _ in range(4)]).arrange(RIGHT)
        self.play(FadeTransformPieces(src, dst), run_time=0.9)
        self.wait(0.2)

        # FadeToColor:整体渐变为指定颜色
        self.play(FadeToColor(dst, PURPLE), run_time=0.6)
        self.wait(0.3)

自测

✏️ 练习

self.play(Transform(a, b)) 之后,场景里保存的是 a 还是 b?如果后面想继续对"变换结果"做动画,用哪个更合适?

✅ 参考答案

场景里是 a(a 已变形为 b 的样子),b 本身没有进场景。若后续要操作结果,用 ReplacementTransform(a, b) 更顺手——结束后场景里就是 b,直接对它继续 play 即可。

✏️ 练习

save_state/Restore 与 generate_target/MoveToTarget 都能"让物体去到某个状态",什么情况下必须用前者?

✅ 参考答案

需要去了又回的场景:先 save_state() 留快照,任意折腾后用 Restore 一键还原。MoveToTarget 是单向的,且终态要事先在 target 副本上摆好;Restore 的快照就是"回去的终点",无需再编辑。

下一步

整体变形之外,还有一类"有脑子的变换"——能自动匹配两物体的组成部分,让相同的部分原地保留、不同的部分精准对换:匹配变换。