Manim是一个由3Blue1Brown的Grant Sanderson开发的开源框架,用户可以通过编写Python代码来创建数学动画,适用于教学、科研和科普宣传等多个领域。
Manim的核心功能之一是动画效果的创建和控制。它提供了多种动画效果,如创建、变换、淡入淡出等,用户可以通过简单的代码实现复杂的动画效果,此外还支持自定义动画效果,以满足用户的特定需求。
Manim官网:Manim Community
在安装Manim前需要先安装ffmpeg和MikTeX。
ffmpeg下载网址:Download FFmpeg
MikTeX下载网址:Getting MiKTeX
下载完成后需要配置下Path环境
我为Manim使用Anaconda创建了一个3.10版本的Python虚拟环境,随后在虚拟环境中直接安装Manim:
pip install manim
有些时候运行Manim脚本会产生报错,只要在 MikTeX Console 中安装相应的宏包即可
接下来整三段官方教学代码和一段我写的Jumping第一定律教学动画
代码一: 绘制文本
这里先创建一个BraceAnnotation
类,它继承自 Scene
类,这是 Manim 中用于构建动画场景的基础类。随后创建场景construct,将对象添加到场景中即可:
from manim import *
class BraceAnnotation(Scene):
def construct(self):
dot = Dot([-2, -1, 0])
dot2 = Dot([2, 1, 0])
line = Line(dot.get_center(), dot2.get_center()).set_color(ORANGE)
b1 = Brace(line)
b1text = b1.get_text("Horizontal distance")
b2 = Brace(line, direction=line.copy().rotate(PI / 2).get_unit_vector())
b2text = b2.get_tex("x-x_1")
self.add(line, dot, dot2, b1, b2, b1text, b2text)
运行效果如下:
输入命令可以将脚本转换为动画:
manim test.py BraceAnnotation # 类名
脚本运行后,会生成一系列文件夹,在video文件夹中可以找到生成的动画
代码二:图形动画
这里展示了两个椭圆之间的布尔运算。先创建两个不同颜色的椭圆,并添加了标题文本,接着依次通过动画展示了这两个椭圆的交集、并集、排除和差集,同时为每种运算结果添加了相应的文本标签。
from manim import *
class BooleanOperations(Scene):
def construct(self):
ellipse1 = Ellipse(
width=4.0, height=5.0, fill_opacity=0.5, color=BLUE, stroke_width=10
).move_to(LEFT)
ellipse2 = ellipse1.copy().set_color(color=RED).move_to(RIGHT)
bool_ops_text = MarkupText("<u>Boolean Operation</u>").next_to(ellipse1, UP * 3)
ellipse_group = Group(bool_ops_text, ellipse1, ellipse2).move_to(LEFT * 3)
self.play(FadeIn(ellipse_group))
i = Intersection(ellipse1, ellipse2, color=GREEN, fill_opacity=0.5)
self.play(i.animate.scale(0.25).move_to(RIGHT * 5 + UP * 2.5))
intersection_text = Text("Intersection", font_size=23).next_to(i, UP)
self.play(FadeIn(intersection_text))
u = Union(ellipse1, ellipse2, color=ORANGE, fill_opacity=0.5)
union_text = Text("Union", font_size=23)
self.play(u.animate.scale(0.3).next_to(i, DOWN, buff=union_text.height * 3))
union_text.next_to(u, UP)
self.play(FadeIn(union_text))
e = Exclusion(ellipse1, ellipse2, color=YELLOW, fill_opacity=0.5)
exclusion_text = Text("Exclusion", font_size=23)
self.play(e.animate.scale(0.3).next_to(u, DOWN, buff=exclusion_text.height * 3.5))
exclusion_text.next_to(e, UP)
self.play(FadeIn(exclusion_text))
d = Difference(ellipse1, ellipse2, color=PINK, fill_opacity=0.5)
difference_text = Text("Difference", font_size=23)
self.play(d.animate.scale(0.3).next_to(u, LEFT, buff=difference_text.height * 3.5))
difference_text.next_to(d, UP)
self.play(FadeIn(difference_text))
代码三:动态展示角度
from manim import *
class MovingAngle(Scene):
def construct(self):
rotation_center = LEFT
theta_tracker = ValueTracker(110)
line1 = Line(LEFT, RIGHT)
line_moving = Line(LEFT, RIGHT)
line_ref = line_moving.copy()
line_moving.rotate(
theta_tracker.get_value() * DEGREES, about_point=rotation_center
)
a = Angle(line1, line_moving, radius=0.5, other_angle=False)
tex = MathTex(r"\theta").move_to(
Angle(
line1, line_moving, radius=0.5 + 3 * SMALL_BUFF, other_angle=False
).point_from_proportion(0.5)
)
self.add(line1, line_moving, a, tex)
self.wait()
line_moving.add_updater(
lambda x: x.become(line_ref.copy()).rotate(
theta_tracker.get_value() * DEGREES, about_point=rotation_center
)
)
a.add_updater(
lambda x: x.become(Angle(line1, line_moving, radius=0.5, other_angle=False))
)
tex.add_updater(
lambda x: x.move_to(
Angle(
line1, line_moving, radius=0.5 + 3 * SMALL_BUFF, other_angle=False
).point_from_proportion(0.5)
)
)
self.play(theta_tracker.animate.set_value(40))
self.play(theta_tracker.animate.increment_value(140))
self.play(tex.animate.set_color(RED), run_time=0.5)
self.play(theta_tracker.animate.set_value(350))
代码四:Jumping第一定律
from manim import Scene, ImageMobject, ORIGIN, FadeIn, FadeOut, Text, config
from manim import *
class JumpingLaw(Scene):
def construct(self):
config.tex_template.add_to_preamble(r'\usepackage[utf8]{inputenc}')
config.tex_template.add_to_preamble(r'\usepackage{amsmath}')
config.tex_template.add_to_preamble(r'\usepackage{amssymb}')
config.tex_template.add_to_preamble(r'\usepackage{unicode-math}')
config.tex_template.add_to_preamble(r'\setmainfont{SimHei}') # 使用黑体字体
mathematician_image = ImageMobject("jumping.png").scale(1)
mathematician_image.move_to(ORIGIN)
text = Text("Jumping第一定律").scale(0.75).next_to(mathematician_image, DOWN)
formula1 = Text("宇宙最终答案 = 42").scale(0.75)
formula2 = Text("一周有 7 天").scale(0.75)
formula3 = Text("42 ÷ 7 = 6").scale(0.75)
formula4 = Text("主 = 6").scale(0.75)
formula1.move_to(3 * LEFT + 2 * UP)
formula2.move_to(3 * LEFT + UP)
formula3.move_to(3 * LEFT)
formula4.move_to(3 * LEFT + DOWN)
self.play(FadeIn(mathematician_image), FadeIn(text))
self.wait()
self.play(
mathematician_image.animate.move_to(np.array([3, 1.5, 0])), # 用3D坐标,z坐标设为0
text.animate.move_to(np.array([3, -2, 0]))
)
self.wait()
self.play(FadeIn(formula1))
self.wait()
self.play(FadeIn(formula2))
self.wait()
self.play(FadeIn(formula3))
self.wait()
self.play(FadeIn(formula4))
self.wait(2)
self.play(
FadeOut(mathematician_image),
FadeOut(text),
FadeOut(formula1),
FadeOut(formula2),
FadeOut(formula3)
)
self.wait()
formula4.scale(2)
formula4.move_to(ORIGIN)
self.play(formula4.animate.scale(2).move_to(ORIGIN))
self.wait(2)
运行后效果如下: