# manimce **Repository Path**: shiliupi/manimce ## Basic Information - **Project Name**: manimce - **Description**: Examples of Scene and Study Notes based on Manim Community Edition - **Primary Language**: Python - **License**: Not specified - **Default Branch**: main - **Homepage**: https://github.com/leekunhwee/ - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 1 - **Created**: 2022-02-03 - **Last Updated**: 2025-08-31 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 欢迎来到 ManimCE 中文教程
## 目录 Table of Contents: - [编程环境配置](#编程环境配置) - [创建虚拟环境](#创建虚拟环境) - [安装依赖文件](#安装依赖文件) - [安装 manim ](#安装-manim) - [安装配置 Jupyter](#安装配置-Jupyter) - [导入 manim 库](#导入-manim-库) - [第一个 Scene](#第一个-Scene) - [定位 Mobjects 并将其移动](#定位-Mobjects-并将其移动) - [方法的动画:`.animate` 语法](#方法的动画) - [排版打印数学公式](#排版打印数学公式) - [一些更专业的例子](#一些更专业的例子)
中文版 **Jupyter Notebook** 演示文件在本库的:First Steps with Manim.ipynb,需要将其下载下来,放到 **Jupyter Notebook** 的默认运行路径下,运行 **Jupyter Notebook**,然后在自动跳转的网页中打开。 > *其他相关资源:* [Documentation](https://docs.manim.community), [Discord](https://discord.gg/mMRrZQW), [Reddit](https://www.reddit.com/r/manim/) ## 编程环境配置 需要安装两个软件: [`Anaconda`](https://www.anaconda.com/products/individual):一个开源的 Python 和 R 语言的发行版本,常用于计算科学,其致力于简化软件包管理系统和部署。 [`MikTeX`](https://miktex.org/download):一款免费的跨平台文字处理系统,包含了 TeX 及其相关程序。 点击软件名即可跳转下载链接,下载后直接按照提示步骤默认安装即可,由于安装过程简单,此处不加赘述。 ## 创建虚拟环境 利用 `Anaconda` 新建开发环境(虚拟环境),这样可以进行多版本的灵活切换,避免很多版本冲突问题。 在 `Anaconda Navigator` 中的 `Environments` 里,点击 `base(root)` 的右边箭头,`Open Terminal` 打开终端,可发现上图的路径前有一个 `(base)`,这说明当前运行环境为主系统环境。 输入 `conda create -n <虚拟环境名>`,回车。这时在 `cmd` 中就可以激活并进入该新建的环境了。 在 `cmd` 中激活该环境可以用 `conda activate <虚拟环境名>` ,取消激活可以用 `conda deactivate`。 :此步骤中使用 `开始 -> Anaconda -> Anaconda Prompt` 也可以实现同 `cmd` 一样的效果。 ## 安装依赖文件 在该虚拟环境下,安装一个 3.9 版本的 Python(当前 manimCE 版支持 3.7-3.9 版本的 Python),输入:`conda install python=3.9`,回车。 再安装提供影视串流功能的 ffmpeg,输入:`conda install ffmpeg`,回车。 ## 安装 manim manim Community Edition 可以使用 pip 工具直接安装,非常方便。直接输入:`pip install manim`,回车。 ## 安装配置 Jupyter 在命令行运行生成视频时代码与结果分离,不易于对照与比较,如果使用 `Jupyter Notebook` 则可以达到很好的视觉效果,也方便记录编程的过程。安装完 `Anaconda` 之后,系统会默认在主系统环境中安装 `Jupyter Notebook`,为了在虚拟环境中使用 `Jupyter Notebook`,需要再在虚拟环境中安装一次。 ### 在虚拟环境中安装 Jupyter 在激活新环境的基础上(可以看到命令行开头处的括号内显示新的环境名),输入 `conda install jupyter notebook`,安装 `Jupyter Notebook`。 为了在虚拟环境中运行 `Jupyter Notebook`,一方面需要在虚拟环境中安装 `Jupyter Notebook`,另一方面,要安装一个插件 `conda install nb_conda`,之后再打开 `Jupyter Notebook`,在 `new` 按钮下将多出不同环境的选项。 **注意,关于 Jupter Notebook 的安装与配置,只有该步是在虚拟环境中进行的,下面的安装、配置均在主系统环境(base)中进行。** ### 更改默认路径 在 Anaconda prompt 的主系统环境(base)中,输入:`jupyter notebook --generate-config` ,可以生成一个路径配置文件,用以配置 `Jupyter` 每次打开时的默认路径。 该新生成的文件路径一般在 `C:\Users\\.jupyter\jupyter_notebook_config.py` 中。 打开该文件,找到 `## The directory to use for notebooks and kernels.` 这一行。将其下 `c.NotebookApp.notebook_dir =` 一行开头的 `#` 删除并添加自己的路径(注意:路径的分隔符要由 `\` 改为 `\\`,或者,在`路径`前加 r),然后保存。 那么下一次从 `Anaconda Navigator` 打开 `Jupyter Notebook` 的时候,将跳转到自定义的路径。(如果还是不行,可能是因为 Jupyter 快捷方式的属性中,路径一行的末尾存在 `%USERPROFILE%`,进而导致设置无效,将 `%USERPROFILE%` 删去即可。) ### 优化编程界面 点击打开 [`Jupyter themes`](https://github.com/dunovank/jupyter-themes) 找到优化方法。 由于 `Anaconda` 中不包含这个包,所以这里需要用 `pip` 进行安装。 在 Anaconda prompt 的主系统环境(base)中,输入: `pip install jupyterthemes` 进行安装,后面可以通过 `pip install --upgrade jupyterthemes` 进行升级。 可参考输入配置参数:`jt -t oceans16 -f fira -fs 17 -cellw 90% -ofs 14 -dfs 14 -T` 进行优化界面配置。 ### 代码自动补全 在 Anaconda prompt 的主系统环境(base)中,输入: ``` pip install jupyter_contrib_nbextensions && jupyter contrib nbextension install ``` 然后运行: ``` jupyter contrib nbextension install --user --skip-running-check ``` 从 `Anaconda Navigator` 打开 `Jupyter Notebook`,会发现多了一个 `Nbextes` 选项,点击并勾选里面的 `Hinterland` 和 `Table of Contents`。 配置完毕! ### 快捷键 * `Enter` :进入编辑模式 * `Esc` :进入命令模式 * `Y` :单元转入代码状态 * `M` :单元转入 `markdown` 状态 * `R` :单元转入 `raw` 状态 * `Ctrl+Enter` :运行 * `O` :展开折叠输出 * `H` :显示快捷键 * `A` :在上方插入代码块 * `B` :在下方插入代码块 * `D,D`:删除代码块 ## 导入 manim 库 打开 `Jupyter Notebook`,在虚拟环境中新建文件,或打开已有文件后通过 `Kernel -> Change Kernel -> Python [conda env:<虚拟环境名>]`。 在代码块中输入 `import *` 来导入 manim 中的所有库。点击 *Run* 按钮或 `Shift`+`Enter` 或 `Ctrl`+`Enter` 来运行。 第二行是用来控制输出视频的展示画面的宽度,可根据自己的需要进行调整。 ```python from manim import * config.media_width = "60%" ```
Manim Community v0.13.1

如果运行成功,将会输出已安装的 manim 的版本。 ## 第一个 Scene manim 通过实例化 *Scenes* 来生成视频。这些 *Scenes* 实际上是一系列特殊 *类* 的实例,它们都拥有一个用以描述动画的 `construct` 方法。(为了方便本教程的描述,这里没有顾及读者是否熟悉 Python 或诸如 *类* 、 *方法* 等面向对象语言的专业术语,不过如果读者想继续学习 manim,有必要先学习一下 Python 编程基础。) 运行下面的例子,查看输出的视频。 ```python %%manim -v WARNING -qm CircleToSquare class CircleToSquare(Scene): def construct(self): blue_circle = Circle(color=BLUE, fill_opacity=0.5) green_square = Square(color=GREEN, fill_opacity=0.8) self.play(Create(blue_circle)) self.wait() self.play(Transform(blue_circle, green_square)) self.wait() ```

Circle To Square

尽管这个例子中大部分代码看上去是不言自明是,这里仍需要一步步详细地进行说明。 首先, ``` %%manim -v WARNING -qm CircleToSquare ``` 是一个 *魔术指令 (magic command)*,该指令仅对 `Jupyter notebook` 有效。这条命令等价于在终端调用 manim 命令。 `-v WARNING` 将隐去所有暂时用不到的输出信息 (如果你想看这些信息,可以将命令中的这部分代码去掉)。标志 `-qm` 控制输出视频的画面质量,它是 `--quality=m` 的缩写,即中等质量。这意味着输出的视频将为 30帧 720p。(可以尝试将其改为 `-qh` 或 `-ql` 分别代表 *高 (high)* 、 *低 (low)* 质量。) `CircleToSquare` 为 *Scene* 类实例化之后的名称。之后几句分别为: ```py class CircleToSquare(Scene): def construct(self): [...] ``` `def` 后面的这个整体定义了这个名为 `CircleToSquare` 的 manim 实例,该实例中的 `construct` 方法中精确描述了将展现怎样的动画内容及效果,其可被看做是一张描绘着该动画内容及效果的 *蓝图*。 ```py blue_circle = Circle(color=BLUE, fill_opacity=0.5) green_square = Square(color=GREEN, fill_opacity=0.8) ``` 前两行分别创建了名为 `Circle` 和 `Square` 的两个 Mobject 对象,它们分别拥有各自的颜色及填充透明度。 但是,它们尚未加入到动画场景中。为了展现,需要用到 `self.add`,或者 ... ```py self.play(Create(blue_circle)) self.wait() ``` ... 将一个 manim 对象 (*Mobject*) 添加到场景中的过程以动画形式展现出来。在该方法中 `self` 代表的是当前场景,`self.play(my_animation)` 可以解释为:"*该场景中需要演示我的动画*" 。 `Create` 就是一个动画,不过这里还有很多其他类型的动画 (例如 `FadeIn`,或者 `DrawBorderThenFill`,可以自行尝试!)。调用 `self.wait()` 的目的就如你想的那样,让动画画面暂停一会儿 (默认是 1 秒)。如果改为 `self.wait(2)`,将暂停两秒。 最后两行, ``` self.play(Transform(blue_circle, green_square)) self.wait() ``` 用于展现从蓝色圆圈到绿色方块的变化过程 (并加上 1 秒的暂停)。 ## 定位 Mobjects 并将其移动 新问题:创建一个场景,其中创建圆圈,同时在其下方写出一些文字。可以复用上面给出的蓝色圆圈,然后再加些新代码。 ```python %%manim -v WARNING -qm HelloCircle class HelloCircle(Scene): def construct(self): # blue_circle = Circle(color=BLUE, fill_opacity=0.5) # 此处也可以先创建一个“默认的”圆圈,再通过 set 方法添加自己想要的属性! circle = Circle() blue_circle = circle.set_color(BLUE).set_opacity(0.5) label = Text("A wild circle appears!") label.next_to(blue_circle, DOWN, buff=0.5) self.play(Create(blue_circle), Write(label)) self.wait() ```

Hello Circle

显然,文字可以使用名为 `Text` 的 Mobject 对象来创建,其所处位置由下面一行代码实现: ```py label.next_to(blue_circle, DOWN, buff=0.5) ``` Mobject 对象有好几种方法实现定位,`next_to` 是其中之一 (`shift`, `to_edge`, `to_corner`, `move_to` 则是另外一些 – 具体可通过搜索框查阅 [documentation](https://docs.manim.community/) )。对于 `next_to` 方法,第一个参数 (`blue_circle`) 代表 `label` 所附着的 Mobject 对象。第二个参数(`DOWN`),描述了附着的方向 (可以改为 `LEFT`,`UP`,或 `RIGHT` 试试!)。最后,`buff=0.5` 控制了 `blue_circle` 和 `label` 这两个 Mobject 间的距离,增大赋值将使得 `label` 向下方远离 `blue_circle`。 另外需要注意的是,这里 `self.play` 的调用形式也发生了改变:可以将多个动画参数同时赋予 `self.play` 方法,那么这些动画将同步展现。如果想一个接着一个地展现,那么就将 `self.play` 方法分开来写为几行: ```py self.play(Create(blue_circle)) self.play(Write(label)) ``` 并看看有什么变化。 顺便说一下,Mobject 对象自带与定位无关的方法:例如,为了得到蓝色圆圈,也可以创建一个默认的圆圈,然后设置其颜色和不透明度: ```py circle = Circle() blue_transparent_circle = circle.set_color(BLUE) blue_circle = blue_transparent_circle.set_opacity(0.5) ``` 也可将上面的代码简写为: ```py blue_circle = Circle().set_color(BLUE).set_opacity(0.5) ``` 像这样,可以直接在名为 `Circle` 的圆圈上附加属性。 ## 方法的动画 在上一个例子中,用到了 `.next_to` 方法,这是一种修改 Mobject 对象位置的方法。但如何去演示出这些方法对 Mobject 对象的作用过程,或者说方法 `.shift` 、 `.rotate` 或者 `.scale` 到底是如何影响一个 Mobject 对象的?那么,`.animate` 语法就是该问题的答案,让我们看看这个例子。 ```python %%manim -v WARNING -qm CircleAnnouncement class CircleAnnouncement(Scene): def construct(self): blue_circle = Circle(color=BLUE, fill_opacity=0.5) announcement = Text("Let us draw a circle.") self.play(Write(announcement)) self.wait() self.play(announcement.animate.next_to(blue_circle, UP, buff=0.5)) self.play(Create(blue_circle)) ```

Circle Announcement

在一般通过 `announcement.next_to(blue_circle, UP, buff=0.5)` 来实现定位的地方,前置调用一个 `.animate` 方法,将其后的方法转为动画,并通过 `self.play` 方法演示出了。下面的例子展现了几种方法的作用于 Mobject 对象的过程动画: ```python %%manim -v WARNING -qm AnimateSyntax class AnimateSyntax(Scene): def construct(self): triangle = Triangle(color=RED, fill_opacity=1) self.play(DrawBorderThenFill(triangle)) self.play(triangle.animate.shift(LEFT)) self.play(triangle.animate.shift(RIGHT).scale(2)) self.play(triangle.animate.rotate(PI/3)) self.play(triangle.animate.set_color(GREEN)) ```

Animate Syntax

第一个 `.play` 方法创建了一个三角形;第二个 `.play` 将该三角形往左移动一个单位;第三个 `.play` 将其向右移回去,并同时放大到原来的两倍;第四个 `.play` 将其逆时针旋转 $\pi/3$ (可以修改一些参数,并再次运行上面的代码,例如 `set_color`)。 ```python ``` 当你仔细看上面那个例子时,将会发现,旋转过程并不是真正的 “旋转”,而是直接变换到了转动后的一个版本。但其三个角划过的并不是圆弧(尽管其本应绕着三角形的中心旋转),而是几条直线段。这个动画给人留下的印象是:该三角形先缩小然后又扩大。 这并非是一个 **bug**,纯属 `.animate` 语法作用的效果:动画是根据初始 (Mobject 对象 `triangle`) 和最终 (旋转后的 Mobject 对象 `triangle.rotate(PI/3)`) 的两个特定状态构建。manim 试图通过插值的方式将这两种状态联系起来,但却不知道如何平滑过渡。下面的例子将清晰地说明这一点: ```python %%manim -v WARNING -qm DifferentRotations class DifferentRotations(Scene): def construct(self): left_square = Square(color=BLUE, fill_opacity=0.7).shift(2*LEFT) right_square = Square(color=GREEN, fill_opacity=0.7).shift(2*RIGHT) self.play(left_square.animate.rotate(PI), Rotate(right_square, angle=PI), run_time=2) self.wait() ```

Different Rotations

## 排版打印数学公式 manim 支持 LaTeX,该标识语言常用于数学排版中。详情请见 [$LaTeX$ 30 分钟教程](https://www.overleaf.com/learn/latex/Learn_LaTeX_in_30_minutes). 下面是一些在 manim 中使用 $LaTeX$ 的实例: ```python %%manim -v WARNING -qm CauchyIntegralFormula class CauchyIntegralFormula(Scene): def construct(self): formula = MathTex(r"[z^n]f(z) = \frac{1}{2\pi i}\oint_{\gamma} \frac{f(z)}{z^{n+1}}~dz") self.play(Write(formula), run_time=3) self.wait() ```

Cauchy Integral Formula

正如该例所示,`MathTex` 对象可以被赋予简单的 (数学模式) $LaTeX$ 字符串。如果想赋予“一般模式”的 $LaTex$,则使用 `Tex`。 当然,manim 也可以实现排版公式转换的可视化。如下面的例子所示: ```python %%manim -v WARNING -qm TransformEquation class TransformEquation(Scene): def construct(self): eq1 = MathTex("42 {{ a^2 }} + {{ b^2 }} = {{ c^2 }}") eq2 = MathTex("42 {{ a^2 }} = {{ c^2 }} - {{ b^2 }}") eq3 = MathTex(r"a^2 = \frac{c^2 - b^2}{42}") self.add(eq1) self.wait() self.play(TransformMatchingTex(eq1, eq2)) self.wait() self.play(TransformMatchingShapes(eq2, eq3)) self.wait() ```

Transform Equation

在上面的例子中,`eq1` 和 `eq2` 有一些双花括号的位置,在通常情况下,$LaTeX$ 不会这样表达。这是一种特殊的 manim 符号,它以一种特殊的方式将生成的 Tex 对象' `eq1` 和 `eq2` 分组。 这种特殊的符号在使用 `TransformMatchingTex` 方法时很有用:它会将具有同样 TeX 字符串的部分相互转换 (例如,`a^2` 到 `a^2`),如果没有这种特殊的符号,这个等式就会被当作一个很长的 TeX 字符串。相比之下,`TransformMatchingShapes` 方法就不那么智能了:它只是简单地尝试将“看起来相同”的形状转换成彼此的形状,尽管如此,它仍然非常有用。 如果您已经了解到这里,您应该对库的基本用法有了初步的印象。您可以在下面的例子中找到一些更高级的示例,它们说明了一些更专业的概念。继续,试着尝试并修改它们,就像你对上面的那些做的那样!请浏览文档 [documentation](https://docs.manim.community),了解已经实现的内容,如果您想自己构建一些更复杂的对象,请查看源代码。 社区 [community](https://www.manim.community/discord/) 非常欢迎问题咨询,并且我们希望您能够分享自己编写的令人称奇的项目。**Happy *manimating*!** ## 一些更专业的例子 在深入研究这些示例之前:请注意,它们用更加专业的概念让您了解如何设置和编写更为复杂的场景。这些例子没有附带额外的解释,它们**不打算作为(入门级)学习资源**。 ```python %%manim -v WARNING -qm FormulaEmphasis class FormulaEmphasis(Scene): def construct(self): product_formula = MathTex( r"\frac{d}{dx} f(x)g(x) =", r"f(x) \frac{d}{dx} g(x)", r"+", r"g(x) \frac{d}{dx} f(x)" ) self.play(Write(product_formula)) box1 = SurroundingRectangle(product_formula[1], buff=0.1) box2 = SurroundingRectangle(product_formula[3], buff=0.1) self.play(Create(box1)) self.wait() self.play(Transform(box1, box2)) self.wait() ```

Formula Emphasis

```python %%manim -v WARNING -qm PlotExample class PlotExample(Scene): def construct(self): plot_axes = Axes( x_range=[0, 1, 0.05], y_range=[0, 1, 0.05], x_length=9, y_length=5.5, axis_config={ "numbers_to_include": np.arange(0, 1 + 0.1, 0.1), "font_size": 24, }, tips=False, ) y_label = plot_axes.get_y_axis_label("y", edge=LEFT, direction=LEFT, buff=0.4) x_label = plot_axes.get_x_axis_label("x") plot_labels = VGroup(x_label, y_label) plots = VGroup() for n in np.arange(1, 20 + 0.5, 0.5): plots += plot_axes.plot(lambda x: x**n, color=WHITE) plots += plot_axes.plot( lambda x: x**(1 / n), color=WHITE, use_smoothing=False ) extras = VGroup() extras += plot_axes.get_horizontal_line(plot_axes.c2p(1, 1, 0), color=BLUE) extras += plot_axes.get_vertical_line(plot_axes.c2p(1, 1, 0), color=BLUE) extras += Dot(point=plot_axes.c2p(1, 1, 0), color=YELLOW) title = Title( r"Graphs of $y=x^{\frac{1}{n}}$ and $y=x^n (n=1, 1.5, 2, 2.5, 3, \dots, 20)$", include_underline=False, font_size=40, ) self.play(Write(title)) self.play(Create(plot_axes), Create(plot_labels), Create(extras)) self.play(AnimationGroup(*[Create(plot) for plot in plots], lag_ratio=0.05)) ```

Plot Example

```python %%manim -v WARNING -qm ErdosRenyiGraph import networkx as nx nxgraph = nx.erdos_renyi_graph(14, 0.5) class ErdosRenyiGraph(Scene): def construct(self): G = Graph.from_networkx(nxgraph, layout="spring", layout_scale=3.5) self.play(Create(G)) self.play(*[G[v].animate.move_to(5*RIGHT*np.cos(ind/7 * PI) + 3*UP*np.sin(ind/7 * PI)) for ind, v in enumerate(G.vertices)]) self.play(Uncreate(G)) ```

Erdos Renyi Graph

```python %%manim -v WARNING -qm CodeFromString class CodeFromString(Scene): def construct(self): code = '''from manim import Scene, Square class FadeInSquare(Scene): def construct(self): s = Square() self.play(FadeIn(s)) self.play(s.animate.scale(2)) self.wait() ''' rendered_code = Code(code=code, tab_width=4, background="window", language="Python", font="Monospace") self.play(Write(rendered_code)) self.wait(2) ```

Code From String

```python %%manim -qm -v WARNING OpeningManim class OpeningManim(Scene): def construct(self): title = Tex(r"This is some \LaTeX") basel = MathTex(r"\sum_{n=1}^\infty \frac{1}{n^2} = \frac{\pi^2}{6}") VGroup(title, basel).arrange(DOWN) self.play( Write(title), FadeIn(basel, shift=UP), ) self.wait() transform_title = Tex("That was a transform") transform_title.to_corner(UP + LEFT) self.play( Transform(title, transform_title), LaggedStart(*[FadeOut(obj, shift=DOWN) for obj in basel]), ) self.wait() grid = NumberPlane(x_range=(-10, 10, 1), y_range=(-6.0, 6.0, 1)) grid_title = Tex("This is a grid") grid_title.scale(1.5) grid_title.move_to(transform_title) self.add(grid, grid_title) self.play( FadeOut(title), FadeIn(grid_title, shift=DOWN), Create(grid, run_time=3, lag_ratio=0.1), ) self.wait() grid_transform_title = Tex( r"That was a non-linear function \\ applied to the grid" ) grid_transform_title.move_to(grid_title, UL) grid.prepare_for_nonlinear_transform() self.play( grid.animate.apply_function( lambda p: p + np.array([np.sin(p[1]), np.sin(p[0]), 0]) ), run_time=3, ) self.wait() self.play(Transform(grid_title, grid_transform_title)) self.wait() ```

Opening Manim