位图与 SVG

本节介绍两种“外部资源”图形:像素图片(ImageMobject 家族)与矢量图(SVGMobject 家族)。

AbstractImageMobject

AbstractImageMobject 是所有图片 mobject 的抽象基类,位于 manim.mobject.types.image_mobject 模块(没有导出到 manim 顶级命名空间,需要显式导入)。它负责像素数据的读取、缩放与分辨率换算,日常使用直接用其子类 ImageMobject。

from manim.mobject.types.image_mobject import AbstractImageMobject

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]; "ImageMobject" -> "AbstractImageMobject"; "AbstractImageMobject" -> "Mobject"; }

ImageMobject

ImageMobject(filename_or_array, scale_to_resolution=1080, invert=False, image_mode='RGBA', **kwargs) 官方文档 ↗

ImageMobject 在场景中显示一张位图,图片来源可以是文件路径,也可以直接是内存中的 numpy 数组。

name

type

default

desc

filename_or_array

StrPath | NDArray

—

图片文件路径,或 HxW(xC) 的 numpy 数组

scale_to_resolution

int

1080

构造时把图片高度缩放到该像素数对应的 Manim 高度

invert

bool

False

是否做黑白反转

image_mode

str

RGBA

像素格式,如 RGBA、RGB、L

ImageMobject("photo.png")              # 从文件加载
ImageMobject(np.zeros((64, 64, 4), dtype=np.uint8))  # 从内存数组构造

ImageMobject 直接用 numpy 数组构造 64×64 棋盘格,无需任何外部图片文件;scale_to_resolution 保证像素图在不同分辨率下尺寸一致,之后用 scale / .animate 控制大小。

查看源码 image_array_demo.py
"""ImageArrayDemo: ImageMobject built from an in-memory numpy array."""

import numpy as np

from manim import *


class ImageArrayDemo(Scene):
    def construct(self):
        # Build a 64x64 RGBA checkerboard in memory — no external file needed.
        n = 64
        grid = np.indices((n, n)).sum(axis=0) % 2
        rgb = np.zeros((n, n, 4), dtype=np.uint8)
        rgb[grid == 0] = (88, 196, 221, 255)  # TEAL-ish
        rgb[grid == 1] = (10, 20, 68, 255)  # dark navy

        image = ImageMobject(rgb)
        image.scale(3)

        self.play(FadeIn(image))
        self.play(image.animate.rotate(PI / 8).shift(LEFT * 2))
        self.wait(1)

常见坑:

  • 数组构造时 dtype 必须是 np.uint8,通道顺序为 RGBA;数组必须是三维(高 × 宽 × 通道)。

  • ImageMobject 是像素图,set_stroke / set_fill 对它无效;想“框住”图片请叠一个 SurroundingRectangle。

SVGMobject

SVGMobject(file_name=None, should_center=True, height=2, width=None, color=None, opacity=None, fill_color=None, fill_opacity=None, stroke_color=None, stroke_opacity=None, stroke_width=None, svg_default=None, path_string_config=None, use_svg_cache=True, **kwargs) 官方文档 ↗

SVGMobject 把 SVG 文件解析为矢量 mobject:路径、填充、描边都会尽量保留,之后可以像普通 VMobject 一样着色、变换。

name

type

default

desc

file_name

str | PathLike | None

None

SVG 文件路径(相对渲染时的当前工作目录)

should_center

bool

True

是否把图形居中到原点

height

float | None

2

构造后缩放到该高度;None 保持原尺寸

width

float | None

None

缩放到该宽度;与 height 同时给时按最后逻辑生效

color

ParsableManimColor | None

None

整体描边色覆盖

opacity

float | None

None

整体不透明度覆盖

fill_color / fill_opacity

— | None

None

填充覆盖

stroke_color / stroke_opacity / stroke_width

— | None

None

描边覆盖

svg_default

dict | None

None

解析 SVG 时的默认样式(如 {"fill_color": ...})

path_string_config

dict | None

None

路径字符串解析配置

use_svg_cache

bool

True

是否缓存解析结果(按文件内容哈希)

star = SVGMobject("examples/_shared/assets/sample.svg", height=3)
star.set_fill(YELLOW, opacity=1)

从 examples/_shared/assets/sample.svg 加载 SVG,先按文件原样 DrawBorderThenFill 入场,再整体替换为黄色填充、红色描边的版本并缩小。CI 从仓库根目录运行 manim,因此这里使用仓库相对路径。

查看源码 svg_mobject_demo.py
"""SvgMobjectDemo: load examples/_shared/assets/sample.svg and restyle it."""

from manim import *


class SvgMobjectDemo(Scene):
    def construct(self):
        star = SVGMobject("examples/_shared/assets/sample.svg", height=3)
        self.play(DrawBorderThenFill(star))
        self.wait(0.5)

        star_colored = SVGMobject(
            "examples/_shared/assets/sample.svg",
            height=3,
            fill_color=YELLOW,
            stroke_color=RED,
            stroke_width=6,
        ).shift(RIGHT * 0.2)
        self.play(Transform(star, star_colored))
        self.play(star.animate.shift(LEFT * 2).scale(0.6))
        self.wait(0.5)

常见错误

SVGMobject 的 file_name 是相对当前工作目录的路径,不是相对场景文件的路径。本地运行和 CI 的工作目录可能不同,跨环境复现请使用仓库根目录相对的完整路径(如 examples/_shared/assets/sample.svg)。

提示

SVG 文件内容有改动而画面没变?use_svg_cache=True 时解析结果按文件内容哈希缓存,正常会自动失效;如果仍看到旧图形,传 use_svg_cache=False 排查,或清理媒体缓存目录。

VMobjectFromSVGPath

VMobjectFromSVGPath(path_obj, long_lines=False, should_subdivide_sharp_curves=False, should_remove_null_curves=False, **kwargs) 官方文档 ↗

VMobjectFromSVGPath 从单个 SVG 路径对象(svgelements.Path)构造 mobject,是 SVGMobject 解析每一段路径时使用的内部类,也被 Brace 用来把大括号 SVG 路径变成矢量图形。

name

type

default

desc

path_obj

se.Path

—

svgelements 路径对象

long_lines

bool

False

是否把长直线段细分为多段

should_subdivide_sharp_curves

bool

False

是否细分急转弯曲线

should_remove_null_curves

bool

False

是否移除零长度曲线

日常几乎不需要手动构造;理解它有助于排查 SVG 解析问题(例如急角显示异常时开启 should_subdivide_sharp_curves)。

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]; "VMobjectFromSVGPath" -> "VMobject"; }

常见错误与建议

提示

位图与矢量的选择:需要照片、截图、渐变时选 ImageMobject;需要无损缩放、按路径着色时选 SVGMobject。两者都支持 FadeIn、.animate 等所有通用动画。

自测

✏️ 练习

场景要展示一张临时生成的热力图(代码里用 numpy 算出来的 2D 数组),又不想写临时图片文件,怎么做?

✅ 参考答案

把 2D 数据归一化后映射成 RGBA 的 np.uint8 数组,直接传给 ImageMobject:

data = np.random.rand(64, 64)
rgba = np.zeros((64, 64, 4), dtype=np.uint8)
rgba[..., 0] = (data * 255).astype(np.uint8)   # 红通道表示数值
rgba[..., 3] = 255                             # 不透明
self.add(ImageMobject(rgba))

下一步

最后一节介绍颜色系统:ManimColor、调色板常量与颜色工具函数。