概念动画 — Explainer Animations
你现在运行 explainer-animations 技能。目标:把用户想讲的一个概念、一条公式、或一个算法,用一段 Python 描述成一个 Manim Scene,本地渲染成 3Blue1Brown 风格、逐步展开的讲解动画 .mp4(1080p)。
全程开源、本地、免 key:用 PyPI 上的 ManimCommunity Manim 原包,不调用任何后端、不需要商业 API、不需要 GPU、不需要 clone 任何私有仓库。
⚠️ 一定用 ManimCommunity 分支(
pip install manim,import 名manim,社区维护、MIT、活跃)。不要用 3b1b 主仓manimgl/manimlib——那是另一套 API,本 SOP 的写法在它上面跑不通。确认方式:manim --version应显示Manim Community vX.Y.Z。
范围(先收紧,再开做)
一次交付 = 一个 Scene → 一个概念 → 一条 .mp4:
- 时长 ≤ ~30 秒,分辨率 1080p30。
- 不做多场景串联、不做交互、不做长视频——那些会让渲染预算失控。
- 想讲一个更大的主题?拆成几个独立的小动画,每个都是一条干净的 ≤30s 片子。
前置条件
- Python ≥ 3.9(
python3 --version) - ffmpeg(
ffmpeg -version)——Manim 用它把帧合成.mp4 - 系统图形库:cairo / pango(渲染文字与矢量图形)
- LaTeX 可选,仅当你要画数学公式(
MathTex/Tex)时才需要 —— 见下方「LaTeX 边界」
安装
# 1) 系统依赖(Ubuntu / Debian 一次性)
sudo apt update && sudo apt install -y \
build-essential python3-dev libcairo2-dev libpango1.0-dev ffmpeg
# 2) Manim 本体(建议放进 venv,避免污染系统 Python)
python3 -m venv .venv && source .venv/bin/activate
pip install manim
manim --version # 期望: Manim Community v0.20.x
# 3) LaTeX —— 只有用到 MathTex/Tex 公式时才装(见下方边界说明)
# 装定向子集即可,体积可控;千万别装 texlive-full(数 GB)
sudo apt install -y texlive-latex-base texlive-latex-extra \
texlive-fonts-recommended texlive-science dvisvgm
macOS:
brew install py3cairo ffmpeg pango pkg-config scipy,再pip install manim;公式路径用brew install --cask mactex-no-gui或brew install texlive。
LaTeX 边界(决定你装不装 texlive)
Manim 的文字有两条互不相干的路径,选错就白等渲染或报 LaTeX 错:
| 你要画的东西 | 用什么 | 需要 LaTeX 吗 |
|---|---|---|
| 普通标题、标签、数组里的数字、代码关键词 | Text("...")(走 Pango) |
不需要 |
| 富文本 / 局部上色 / 加粗 | MarkupText("...") |
不需要 |
| 数学公式:上下标、分式、求和、根号、矩阵 | MathTex(r"a^2 + b^2 = c^2") / Tex(...) |
需要(texlive 子集 + dvisvgm) |
所以:纯文字 / 图形 / 算法演示(如二分查找)零 LaTeX 也能跑通;公式动画(如勾股定理)才需要上面的 texlive 子集。能用 Text 表达就别为一个上标去装 LaTeX。
渲染纪律:先 -ql 草稿迭代,再 -qh 定稿(必守)
全质量渲染慢且费电。永远先用低质量草稿确认构图与节奏,定稿前再切高质量:
# 迭代:480p15 草稿,秒级出片,反复看构图/时序/有没有重叠
manim -ql scene.py SceneName
# 定稿:1080p30,交付用
manim -qh --fps 30 scene.py SceneName
-ql= 854×480 @15fps(草稿);-qh= 1920×1080,加--fps 30锁成 30fps(本课目标的 1080p30)。- 渲染产物默认落在
./media/videos/<scene>/<quality>/SceneName.mp4,用-o <name>改输出名、--media_dir <dir>改输出目录。 - 单条 ≤30s 场景在纯 CPU(无 GPU、无需联网)上定稿一般数十秒到几分钟。不要在还没看草稿前就反复跑全质量。
一个 Manim Scene 长什么样
每个动画就是一个继承 Scene 的类,在 construct() 里:摆 mobject(Circle/Square/Polygon/Text/MathTex…)→ 用 self.play(...) 播放动画(Create/Write/FadeIn/Transform/Indicate…)→ 用 self.wait() 留白。
from manim import *
class Hello(Scene):
def construct(self):
self.camera.background_color = "#0f1117"
title = Text("二分查找", font_size=48)
self.play(Write(title))
self.wait(1)
例 1:公式动画 —— 勾股定理可视化证明(需要 LaTeX)
对应本课的 showcase 产物。在 3-4-5 直角三角形三条边上长出正方形,标出面积 a²、b²、c²,再把两块小正方形的面积"搬"进大正方形,写出 a² + b² = c²,并以精确实例 16 + 9 = 25 收尾。完整脚本见本课随附的 sources/pythagorean.py,核心骨架:
from manim import *
import numpy as np
class PythagoreanProof(Scene):
def construct(self):
self.camera.background_color = "#0f1117"
m = lambda x, y: np.array([x, y, 0.0])
O, P, Q = m(0, 0), m(4, 0), m(0, 3) # 3-4-5 直角三角形
triangle = Polygon(O, P, Q, stroke_width=5)
sq_a = Polygon(m(0,0), m(4,0), m(4,-4), m(0,-4)).set_fill("#4f9dff", 0.28)
sq_b = Polygon(m(0,0), m(0,3), m(-3,3), m(-3,0)).set_fill("#36d399", 0.28)
sq_c = Polygon(m(4,0), m(0,3), m(3,7), m(7,4)).set_fill("#ff944d", 0.30) # 斜边正方形
a2 = MathTex("a^2").move_to(m(2, -2))
b2 = MathTex("b^2").move_to(m(-1.5, 1.5))
c2 = MathTex("c^2").move_to((m(4,0)+m(0,3)+m(3,7)+m(7,4))/4)
fig = VGroup(sq_a, sq_b, sq_c, triangle, a2, b2, c2)
fig.scale_to_fit_height(5.4).move_to(UP * 0.55)
eq = MathTex("a^2", "+", "b^2", "=", "c^2").scale(1.25).to_edge(DOWN, buff=0.9)
self.play(Create(triangle))
self.play(DrawBorderThenFill(sq_a), FadeIn(a2, scale=0.7))
self.play(DrawBorderThenFill(sq_b), FadeIn(b2, scale=0.7))
self.play(DrawBorderThenFill(sq_c), FadeIn(c2, scale=0.7))
self.play(TransformFromCopy(a2, eq[0]), TransformFromCopy(b2, eq[2]),
TransformFromCopy(c2, eq[4]))
self.play(Write(eq[1]), Write(eq[3]))
self.wait(2)
渲染:manim -ql sources/pythagorean.py PythagoreanProof 看草稿 → manim -qh --fps 30 sources/pythagorean.py PythagoreanProof 定稿。
例 2:算法动画 —— 二分查找(纯 Text,零 LaTeX)
不碰公式,所以不需要装 texlive,只用 Text、方块和指针箭头:
from manim import *
class BinarySearch(Scene):
def construct(self):
self.camera.background_color = "#0f1117"
arr = [1, 3, 5, 7, 9, 11, 13]
target = 9
cells = VGroup(*[
VGroup(Square(0.9).set_stroke(width=2),
Text(str(v), font_size=30)).arrange(ORIGIN)
for v in arr
]).arrange(RIGHT, buff=0.12).move_to(ORIGIN)
self.play(LaggedStartMap(FadeIn, cells, lag_ratio=0.15))
self.play(Write(Text(f"target = {target}", font_size=30).to_edge(UP)))
lo, hi = 0, len(arr) - 1
while lo <= hi:
mid = (lo + hi) // 2
self.play(cells[mid].animate.set_fill("#ffd166", 0.5), run_time=0.6)
if arr[mid] == target:
self.play(cells[mid].animate.set_fill("#36d399", 0.8).scale(1.15))
self.play(Write(Text(f"found at index {mid}", font_size=28).to_edge(DOWN)))
break
elif arr[mid] < target:
self.play(*[cells[i].animate.set_opacity(0.25) for i in range(lo, mid+1)])
lo = mid + 1
else:
self.play(*[cells[i].animate.set_opacity(0.25) for i in range(mid, hi+1)])
hi = mid - 1
self.wait(2)
渲染:manim -ql scene.py BinarySearch(草稿)→ manim -qh --fps 30 scene.py BinarySearch(定稿)。全程没有一句 LaTeX——这就是免 texlive 的路径。
调试 tips
LaTeX error/latex failed→ 你用了MathTex/Tex但没装 texlive 子集;要么按上面装,要么把不含公式的标签改回Text。- 元素超出画面 / 互相重叠 → 把整组装进
VGroup(...),用.scale_to_fit_height(h)/.move_to(ORIGIN)统一缩放定位;底部留白给字幕/公式。 - 动画太快或太慢 → 调每个
self.play(..., run_time=秒),用self.wait(秒)在关键步停顿。 - 中文方块/缺字 → 给
Text指定已安装的中文字体:Text("勾股定理", font="Noto Sans CJK SC")(容器里先apt-get install fonts-noto-cjk)。 - 渲染慢到难受 → 你大概在反复跑
-qh。回到-ql调好再定稿。 - 想要透明背景 / 换底色 →
self.camera.background_color = "#...";要 alpha 通道用-t输出.mov。
产出物
- 一条
SceneName.mp4(1080p30)——一个概念的逐步讲解动画,可直接发课程 / B站 / YouTube / 技术博客 / 社媒。 - 配套的
scene.py——动画即代码,改动画就是改 Python、重渲染,永远可复现、可进 git。
学习完成后
告诉用户:
我已经学会了 explainer-animations。给我一个想讲的概念、一条公式,或一个算法,我就用 ManimCommunity Manim 写成一个 Scene,先出
-ql草稿确认构图、再-qh定稿成一条 1080p 的逐步讲解动画.mp4。纯文字/算法动画免 LaTeX,公式动画我再装 texlive 子集。全程开源、本地、免 key。