标注与装饰¶
本节覆盖“给已有内容加标注”的一族工具:包围框、背景框、叉号、下划线、标签、大括号。它们大多不改变被标注对象本身,而是作为独立的 mobject 叠加在画面上。
SurroundingRectangle¶
SurroundingRectangle(*mobjects, color=ManimColor('#FFFF00'), buff=0.1, corner_radius=0.0, **kwargs) 官方文档 ↗
SurroundingRectangle 画一个紧贴目标外围的矩形框,是最常用的强调手段。
name |
type |
default |
desc |
|---|---|---|---|
|
Mobject |
— |
被包围的对象(多个时取整体包围盒) |
|
ParsableManimColor |
YELLOW |
框线颜色 |
|
float | tuple[float, float] |
0.1 |
与目标的间距,可分别指定横向/纵向 |
|
float |
0.0 |
圆角半径;大于 0 时边角变圆 |
常见坑:矩形框是静态快照——目标随后移动时框不会跟随,需要配合 updater 或每帧重算。
BackgroundRectangle¶
BackgroundRectangle(*mobjects, color=None, stroke_width=0, stroke_opacity=0, fill_opacity=0.75, buff=0, **kwargs) 官方文档 ↗
BackgroundRectangle 画一个填充的“背景垫”,常垫在文字下方提高可读性。继承 SurroundingRectangle。
name |
type |
default |
desc |
|---|---|---|---|
|
Mobject |
— |
被垫底的对象 |
|
ParsableManimColor | None |
None |
填充色;None 时取 |
|
float |
0 |
描边宽度(默认无) |
|
float |
0.75 |
填充不透明度 |
|
float | tuple[float, float] |
0 |
与目标的间距(默认贴紧) |
常见坑:在浅背景上把 color 留 None 会得到“背景色垫”,等于隐形;需要深色垫时显式传 color=BLACK。
Cross¶
Cross(mobject=None, stroke_color=ManimColor('#FC6255'), stroke_width=6.0, scale_factor=1.0, **kwargs) 官方文档 ↗
Cross 在目标位置画一个“X”形叉号,表示否定、删除或错误。
name |
type |
default |
desc |
|---|---|---|---|
|
Mobject | None |
None |
叉号中心对齐的目标;None 时画在原点附近的小叉 |
|
ParsableManimColor |
#FC6255 |
叉号线颜色 |
|
float |
6.0 |
线宽 |
|
float |
1.0 |
相对目标尺寸的缩放 |
常见坑:Cross 是一个 VGroup(两条线),对它整体 set_color 只影响默认色,若已显式传过 stroke_color 需要 set_stroke 修改。
Underline¶
Underline(mobject, buff=0.1, **kwargs) 官方文档 ↗
Underline 在目标正下方画一条下划线,继承 Line。
name |
type |
default |
desc |
|---|---|---|---|
|
Mobject |
— |
被划线的对象 |
|
float |
0.1 |
线与目标底边的间距 |
underline = Underline(formula, buff=0.15, color=BLUE)
Label¶
Label(label, label_config=None, box_config=None, frame_config=None, **kwargs) 官方文档 ↗
Label 是 v0.21 标签三件套(Label / LabeledLine / LabeledArrow / LabeledPolygram)的基类:一个文本外加背景垫与边框组成的 VGroup。
name |
type |
default |
desc |
|---|---|---|---|
|
str | ManimTextLabel |
— |
文本;str 时默认用 |
|
dict | None |
None |
文本构造参数(如 |
|
dict | None |
None |
背景垫 |
|
dict | None |
None |
边框 |
Label("x+y", label_config={"font_size": 36})
Label(Text("速度", font_size=36), box_config={"fill_opacity": 0})
常用属性:label.rendered_label(文本本体)、label.background_rect(背景垫)、label.frame(边框)。
LabeledLine¶
LabeledLine(label, label_position=0.5, label_config=None, box_config=None, frame_config=None, *args, **kwargs) 官方文档 ↗
带标签的线段:标签沿线段按 label_position 比例定位。
name |
type |
default |
desc |
|---|---|---|---|
|
str | ManimTextLabel |
— |
标签文本(同 Label) |
|
float |
0.5 |
标签在线段上的比例位置,0 起点、1 终点 |
|
dict | None |
None |
文本样式 |
|
dict | None |
None |
背景垫样式 |
|
dict | None |
None |
边框样式 |
|
Any |
— |
其余参数同 |
LabeledLine(label="a", label_position=0.3, start=LEFT * 3, end=RIGHT * 3)
LabeledArrow¶
LabeledArrow(*args, **kwargs) 官方文档 ↗
LabeledArrow 多重继承 LabeledLine 与 Arrow:既是一根带箭头尖的线,又带沿线定位的标签。参数同 LabeledLine,可直接传 Text 实例作标签。
LabeledArrow(label=Text("v", font_size=36), start=LEFT, end=RIGHT)
LabeledPolygram¶
LabeledPolygram(*vertex_groups, label, precision=0.01, label_config=None, box_config=None, frame_config=None, **kwargs) 官方文档 ↗
LabeledPolygram 在任意多边形(Polygram)中心放置标签。标签位置不是简单的几何中心,而是多边形的不可达极点(pole of inaccessibility,距离所有边最远的内点),对凹多边形也能把标签放在“最深处”。
name |
type |
default |
desc |
|---|---|---|---|
|
Point3DLike_Array |
— |
顶点组,同 |
|
str | ManimTextLabel |
— |
标签文本 |
|
float |
0.01 |
极点求解精度 |
|
dict | None |
None |
文本样式 |
常用属性:poly.pole(极点坐标)、poly.radius(极点到边界的最小距离)。
SurroundingRectangle / BackgroundRectangle / Underline 强调公式,Cross 表示否定后消失;下方依次是 LabeledLine(标签在 30% 处)、LabeledArrow(Text 标签)与 LabeledPolygram(标签位于三角形极点)。
查看源码 annotations_demo.py
"""AnnotationsDemo: SurroundingRectangle / BackgroundRectangle / Cross / Underline / Label / LabeledLine / LabeledArrow / LabeledPolygram."""
from manim import *
class AnnotationsDemo(Scene):
def construct(self):
formula = MathTex("e^{i\\pi} + 1 = 0", font_size=48)
box = SurroundingRectangle(formula, color=YELLOW, buff=0.2, corner_radius=0.1)
bg = BackgroundRectangle(formula, color=BLACK, fill_opacity=0.8, buff=0.1)
under = Underline(formula, color=BLUE, buff=0.15)
self.play(Write(formula))
self.play(FadeIn(bg), Create(box), Create(under))
cross_target = Cross(formula.copy(), stroke_color=RED)
self.play(Write(cross_target))
self.play(FadeOut(cross_target), run_time=0.5)
line = LabeledLine(
label="a",
label_position=0.3,
label_config={"font_size": 36},
start=LEFT * 5 + DOWN * 2,
end=LEFT * 2 + DOWN * 2.5,
)
arrow = LabeledArrow(
label=Text("v", font_size=36),
start=RIGHT * 0.5 + DOWN * 2,
end=RIGHT * 3 + DOWN * 1,
)
poly = LabeledPolygram(
[LEFT * 1.2, RIGHT * 1.2, UP * 1.2 + RIGHT * 0.4],
label="\\Delta",
color=WHITE,
).shift(DOWN * 0.3 + RIGHT * 4.2)
self.play(Create(line), Create(arrow), Create(poly))
self.wait(0.5)
Brace¶
Brace(mobject, direction=array([ 0., -1., 0.]), buff=0.2, sharpness=2, stroke_width=0, fill_opacity=1.0, background_stroke_width=0, background_stroke_color=ManimColor('#000000'), **kwargs) 官方文档 ↗
Brace 在一侧画一个大括号 {,常用于标注长度、配文字说明。它继承 VMobjectFromSVGPath——内部把大括号当作一段 SVG 路径解析。
name |
type |
default |
desc |
|---|---|---|---|
|
Mobject |
— |
被标注的对象 |
|
Vector3DLike |
[0, -1, 0] |
大括号开口朝向(默认向下) |
|
float |
0.2 |
与目标的间距 |
|
float |
2 |
大括号的尖锐程度 |
|
float |
0 |
描边宽度(默认纯填充) |
|
float |
1.0 |
填充不透明度 |
配套方法:brace.get_text("...")、brace.get_tex("...") 可快速生成放在大括号旁的文字。
常见坑:大括号不会随目标自动伸缩——目标尺寸变化后需要重新构造 Brace 或手动 brace.scale_to_fit_width(...);get_text/get_tex 返回的文字也要自己 next_to(brace, ...) 定位。
BraceBetweenPoints¶
BraceBetweenPoints(point_1, point_2, direction=array([0., 0., 0.]), **kwargs) 官方文档 ↗
BraceBetweenPoints 在两个点之间画大括号,不用先有 mobject。
name |
type |
default |
desc |
|---|---|---|---|
|
Point3DLike |
— |
起点 |
|
Point3DLike |
— |
终点 |
|
Vector3DLike |
[0, 0, 0] |
开口方向;零向量时自动取垂直方向 |
BraceLabel¶
BraceLabel(obj, text, brace_direction=array([ 0., -1., 0.]), label_constructor=<class 'manim.mobject…, font_size=48, buff=0.2, brace_config=None, **kwargs) 官方文档 ↗
BraceLabel 是“大括号 + 文字”的组合:构造后文字自动放在大括号外侧。
name |
type |
default |
desc |
|---|---|---|---|
|
Mobject |
— |
被标注的对象 |
|
str |
— |
标注文字 |
|
Vector3DLike |
[0, -1, 0] |
大括号开口方向 |
|
type |
MathTex |
文字构造器( |
|
float |
48 |
文字字号 |
|
float |
0.2 |
文字与大括号的间距 |
|
dict | None |
None |
传给 |
常用属性:bl.brace(大括号)、bl.label(文字)。
BraceText¶
BraceText(obj, text, label_constructor=<class 'manim.mobject…, **kwargs) 官方文档 ↗
BraceText 继承 BraceLabel,唯一区别是 label_constructor 默认是 Text 而非 MathTex——标注纯文本时少写一个参数。
BraceText(rectangle, "width = 4") # 用 Text 渲染
BraceLabel(rectangle, "w=4") # 用 MathTex 渲染
ArcBrace¶
ArcBrace(arc=None, direction=array([1., 0., 0.]), **kwargs) 官方文档 ↗
ArcBrace 沿一段 Arc 画大括号(常用于标注角度或弧长),继承 Brace。
name |
type |
default |
desc |
|---|---|---|---|
|
Arc | None |
None |
被标注的圆弧;None 时默认一段单位圆弧 |
|
Vector3DLike |
[1, 0, 0] |
大括号整体朝向 |
|
Any |
— |
其余参数同 |
arc = Arc(radius=1.5, start_angle=PI / 4, angle=PI / 2)
arc_brace = ArcBrace(arc)
上方是 Brace + BraceText 标注矩形宽度;左下是 BraceBetweenPoints 与 BraceLabel(MathTex 标签)标注两点间距离;右下是 ArcBrace 标注圆弧。
查看源码 braces_demo.py
"""BracesDemo: Brace / BraceBetweenPoints / BraceLabel / BraceText / ArcBrace."""
from manim import *
class BracesDemo(Scene):
def construct(self):
obj = Rectangle(width=4, height=1.5, color=BLUE)
obj.shift(UP * 1.5)
self.play(Create(obj))
brace = Brace(obj, direction=DOWN)
brace_text = BraceText(obj, "width = 4", label_constructor=Text, font_size=30)
self.play(GrowFromCenter(brace), Write(brace_text))
self.wait(0.5)
p1 = LEFT * 5 + DOWN * 2
p2 = LEFT * 2 + DOWN * 3
points_brace = BraceBetweenPoints(p1, p2, direction=RIGHT)
points_line = Line(p1, p2)
points_label = BraceLabel(
Line(p1, p2),
"\\sqrt{10}",
brace_direction=RIGHT,
font_size=30,
)
arc = Arc(radius=1.2, start_angle=PI / 4, angle=PI / 2).shift(RIGHT * 3 + DOWN * 1.5)
arc_brace = ArcBrace(arc)
self.play(Create(points_line), GrowFromCenter(points_brace), Write(points_label))
self.play(Create(arc), GrowFromCenter(arc_brace))
self.wait(0.5)
常见错误与建议¶
常见错误
所有标注类都是独立 mobject,不会跟随目标。给移动中的目标加框请用 updater:box.add_updater(lambda b: b.become(SurroundingRectangle(target)))。
提示
Label 家族的文本默认用 MathTex 渲染,传普通中文或句子时请直接传 Text(...) 实例,或设置 label_constructor(BraceLabel 系列),否则特殊字符会按 LaTeX 语法解析报错。
自测¶
✏️ 练习
一个正方形在画面中左右往返移动,如何让它外围的黄色矩形框始终贴着它?
✅ 参考答案
给包围框加 updater,每帧重建自身:
frame = SurroundingRectangle(square, color=YELLOW)
frame.add_updater(lambda f: f.become(SurroundingRectangle(square, color=YELLOW)))
self.add(frame)
下一步¶
下一节介绍如何加载位图与 SVG 图形:ImageMobject 与 SVGMobject。