(目录)


一、Manim图形元素的颜色

image.png

上图中以红色RED为例,除了颜色RED本身(默认为RED_A),根据颜色的深浅不同还可分为: RED_A,RED_B,RED_C,RED_D,RED_E 几种颜色。

二、相机(Camera)

相机(Camera)一般用在三维场景中。相机相当于我们看动画的视角,简单来理解的话,相当于我们的眼睛。默认的相机焦点在屏幕的中心位置,相机默认是以俯视的视角查看所有的元素。相机也可以实现动画,它不改变元素在屏幕或者说在坐标系中的位置,通过改变相机的位置和角度来实现动画效果。

1. 移动相机的焦点

改变相机的焦点,焦点在那个元素,那个元素就会在屏幕中心。

# -*- coding: utf-8 -*-
import os
from manim import *


class CameraSample(MovingCameraScene):
    def _move_focus(self):
        s = Square(color=PURE_RED, fill_opacity=0.5)
        t = Triangle(color=PURE_GREEN, fill_opacity=0.5)
        vg = VGroup(s, t)
        vg.arrange(RIGHT, buff=MED_LARGE_BUFF)
        self.add(vg)

        self.play(self.camera.frame.animate.move_to(s))
        self.play(self.camera.frame.animate.move_to(t))
        self.play(self.camera.frame.animate.move_to(vg))

    def construct(self):
        self._move_focus()

        self.wait(10)


# 运行场景
if __name__ == "__main__":
    demo_file = os.path.basename(__file__)
    os.system(f"manim {demo_file} CameraSample -p")

2. 改变相机的视野

改变相机视野就是改变相机与元素的距离,离得越远,物体越小——所谓近大远小。 改变视野通过 self.camera.frame.animate.set方法,通过这个方法设置视野的宽度,可以形成缩放元素的效果。

# -*- coding: utf-8 -*-
import os
from manim import *


class CameraSample(MovingCameraScene):
    def _scale(self):
        s = Square(color=PURE_RED, fill_opacity=0.5)
        self.add(s)

        self.camera.frame.save_state()
        self.play(self.camera.frame.animate.set(width=s.width * 2))
        self.wait(0.3)
        self.play(self.camera.frame.animate.set(width=s.width * 8))
        self.wait(0.3)
        self.play(Restore(self.camera.frame))

    def construct(self):
        self._scale()

        self.wait(10)


# 运行场景
if __name__ == "__main__":
    demo_file = os.path.basename(__file__)
    os.system(f"manim {demo_file} CameraSample -p")

3. 多相机

鹰眼的效果是通过两个相机来实现的,两个相机的焦点一样,但是视野不一样。 鹰眼效果一般用在提供全局视图的场合,特别是当元素特别多的时候。

class CameraSample2(ZoomedScene):
    def __init__(self, **kwargs):
        ZoomedScene.__init__(
            self,
            zoom_factor=3,
            zoomed_display_height=1,
            zoomed_display_width=2,
            image_frame_stroke_width=5,
            zoomed_camera_config={
                "default_frame_stroke_width": 3,
            },
            **kwargs
        )

    def construct(self):
        s = Square(color=RED, fill_opacity=0.5, side_length=1.5)
        t = Triangle(color=GREEN, fill_opacity=0.5).scale(0.5)
        vg = VGroup(s, t)
        vg.arrange(RIGHT, buff=SMALL_BUFF)
        self.add(vg)
        self.activate_zooming(animate=False)
        self.play(s.animate.shift(LEFT), t.animate.shift(RIGHT))
        self.play(s.animate.rotate(2 * PI / 3), t.animate.rotate(PI / 2))
        self.play(s.animate.shift(RIGHT), t.animate.shift(LEFT))
        self.wait(10)

4. 追踪物体

追踪物体就是将相机的焦点定位在移动的物体上,就像我们坐在火车上的感觉一样,那时,我们觉得火车没动,而是车外的风景不断向后移动。

# -*- coding: utf-8 -*-
import os
from manim import *
import numpy as np


class CameraSample(MovingCameraScene):
    def construct(self):
        self.camera.frame.save_state()

        graph = FunctionGraph(
            lambda x: np.sin(x),
            x_range=[-3, 3],
            color=PURE_RED,
        )
        d = Dot(graph.get_start(), color=PURE_BLUE)
        self.add(graph, d)

        self.play(self.camera.frame.animate.scale(0.5).move_to(d))

        def update_curve(mob):
            mob.move_to(d.get_center())

        self.camera.frame.add_updater(update_curve)
        self.play(MoveAlongPath(d, graph), rate_func=linear, run_time=2)
        self.camera.frame.remove_updater(update_curve)

        self.play(Restore(self.camera.frame))

        self.wait(10)


# 运行场景
if __name__ == "__main__":
    demo_file = os.path.basename(__file__)
    os.system(f"manim {demo_file} CameraSample -p")

5. 在3D场景中使用相机

Manim在3D场景中(ThreeDScene)通过方法 move_camera 来改变视角和调整视野。

# -*- coding: utf-8 -*-
import os
from manim import *
import numpy as np


class CameraSample(ThreeDScene):
    def construct(self):
        axes = ThreeDAxes()
        sphere = Surface(
            lambda u, v: np.array(
                [
                    1.5 * np.cos(u) * np.cos(v),
                    1.5 * np.cos(u) * np.sin(v),
                    1.5 * np.sin(u),
                ]
            ),
            v_range=[0, TAU],
            u_range=[-PI / 2, PI / 2],
            checkerboard_colors=[BLUE_D, BLUE_E],
            resolution=(15, 32),
        )
        self.add(axes, sphere)

        self.move_camera(phi=75 * DEGREES, theta=30 * DEGREES)
        self.move_camera(zoom=1.5)
        self.move_camera(zoom=0.5)
        self.wait(10)


# 运行场景
if __name__ == "__main__":
    demo_file = os.path.basename(__file__)
    os.system(f"manim {demo_file} CameraSample -p")

三、基本动画效果

self.add()方法不带动画效果,而self.play()方法是带动画效果的。 可以在self.play()方法中指定如下方法来创建动画效果。

1. Create()

一般用在创建图形上,绘制时图形逐步显示出来。

# -*- coding: utf-8 -*-
import os
from manim import *


class MyAnim(ThreeDScene):

    # 在这里试
    def a_obj(self):
        s = Square(side_length=2, color=PURE_RED)
        self.play(Create(s), run_time=2)

    def construct(self):  # 构造场景
        self.a_obj()
        self.wait(10)


# 运行场景
if __name__ == "__main__":
    demo_file = os.path.basename(__file__)
    os.system(f"manim {demo_file} MyAnim -p")

2. Write()

用在文字的创建上,绘制文字时逐个显示文字。

    def a_obj(self):
        t = Text(
            "Welcome to Manim",
            t2c={"Welcome": BLUE, "Manim": RED},
            t2f={"Manim": "STCaiyun"},
        )
        self.play(Write(t))

3. FadeIn()

是一种逐渐由模糊到清晰的显示方式。

    def a_obj(self):
        s = Square(side_length=2, color=BLUE, fill_opacity=0.6)
        self.play(FadeIn(s))

4. Uncreate()

一般用在擦除图形。

    def a_obj(self):
        s = Square(side_length=2, color=BLUE)
        self.add(s)  # add()没有动画效果
        self.wait(0.5)
        self.play(Uncreate(s))

5. Unwrite()

一般用在擦除文字。

    def a_obj(self):
        t = Text(
            "Welcome to Manim",
            t2c={"Welcome": BLUE, "Manim": RED},
            t2f={"Manim": "STCaiyun"},
        )
        self.add(t)
        self.wait(0.5)
        self.play(Unwrite(t))

6. FadeOut()

是一种逐渐消失的显示方式。

    def a_obj(self):
        s = Circle(radius=2, color=BLUE, fill_opacity=0.6, stroke_width=1)
        self.add(s)
        self.wait(0.5)
        self.play(FadeOut(s))

7. 移动效果

移动的动画有两个函数:

  • shift():移动制定的距离
  • move_to():移动到指定点
    def a_obj(self):
        s = Square(side_length=1, color=BLUE, fill_opacity=0.6)
        self.add(s)
        self.play(s.animate.shift(RIGHT))  # 右移1个单位
        self.play(s.animate.shift(UP))  # 上移1个单位
        self.play(s.animate.shift(LEFT * 2))  # 左移2个单位
        self.play(s.animate.shift(DOWN * 3))  # 下移3个单位
        self.play(s.animate.move_to(ORIGIN))  # 移动到中心处

8. 旋转效果

通过设置角度和旋转的中心来控制旋转效果。

# 自转
s = Square(side_length=2, color=BLUE, fill_opacity=0.6)
self.add(s)
self.play(Rotate(s, angle=2 * PI), run_time=2)  # 自转1周
self.wait(0.5)

# 公转
s = Square(side_length=1, color=BLUE, fill_opacity=0.6).shift(UP)
self.add(s)
# 绕屏幕中心旋转1周
self.play(Rotate(s, angle=2 * PI, about_point=ORIGIN), run_time=2)
self.wait(0.5)

9. 图形变换

变换时既可以保留原图形(TransformFromCopy), 也可以从原图形直接变换成新的图形(ReplacementTransform)。

# 保留原图形
s = Square(side_length=1, color=BLUE, fill_opacity=0.6).shift(LEFT * 2)
c = Circle(radius=1, color=RED, fill_opacity=0.6).shift(RIGHT * 2)
self.add(s)
self.wait(0.5)
self.play(TransformFromCopy(s, c))

# 不保留原图形
s = Square(side_length=1, color=BLUE, fill_opacity=0.6).shift(LEFT * 2)
c = Circle(radius=1, color=RED, fill_opacity=0.6).shift(RIGHT * 2)
self.add(s)
self.wait(0.5)
self.play(ReplacementTransform(s, c))

10. 文字变换

# 保留原文字
t1 = Tex(r"$(a+b)^2$").shift(UP)
t2 = Tex(r"$a^2 + 2ab + b^2$")
self.add(t1)
self.wait(0.5)
self.play(TransformFromCopy(t1, t2))

# 不保留原文字
t1 = Tex(r"$(a+b)^2$").shift(UP)
t2 = Tex(r"$a^2 + 2ab + b^2$")
self.add(t1)
self.wait(0.5)
self.play(ReplacementTransform(t1, t2))