动画是让人们深刻理解数学原理的关键要素,在上一章中已经介绍manim库的基本使用以及静态元素的绘制。本章主要介绍在各种场景下动画的应用以及效果展示。
%%manim PointMovingOnShapes -ql -v WARNING
class PointMovingOnShapes(Scene):
def construct(self):
# 创建一个半径为1的圆
circle = Circle(radius=1, color=BLUE)
# 创建一个点
dot = Dot()
# 将第一个点复制并向右平移1个段位
dot2 = dot.copy().shift(RIGHT)
# 将第一个点加入到场景中
self.add(dot)
# 绘制一个线段
line = Line([3, 0, 0], [5, 0, 0])
# 将线段加入到场景中
self.add(line)
# 增加从重新变大的动画给圆
self.play(GrowFromCenter(circle))
# 增加一个变形动画给dot移动到dot2
self.play(Transform(dot, dot2))
# 增加一个路径引导动画给dot 路径为circle 时间为 2 秒 缓动函数为linear
self.play(MoveAlongPath(dot, circle), run_time=2, rate_func=linear)
# 增加一个旋转中华,旋转中心点为 about_point 持续时间为 1.5 s
self.play(Rotating(dot, about_point=[2, 0, 0]), run_time=1.5)
self.wait()
效果如下
%%manim MovingAround -ql -v WARNING
class MovingAround(Scene):
def construct(self):
square = Square(color=BLUE, fill_opacity=1)
self.play(square.animate.shift(LEFT))
self.play(square.animate.set_fill(ORANGE))
self.play(square.animate.scale(0.3))
self.play(square.animate.rotate(0.4))
self.wait()
效果如下
%%manim MovingAngle -ql -v WARNING
class MovingAngle(Scene):
def construct(self):
# 设定旋转中心为left点[-1,0,0]
rotation_center = LEFT
# 设置一个数值跟踪器 初始值为 110
theta_tracker = ValueTracker(110)
# 绘制一个线段[-1,0][1,0]
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
)
# 绘制一个角度标记在line1与line_moving
a = Angle(line1, line_moving, radius=0.5, other_angle=False)
# 绘制theta的初始位置,此时绘制一个比角标略大的角(弧)通过point_from_proportion(0.5)设定该区域的中间点
te = 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, te)
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))
)
te.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))
# 将数值增加140
self.play(theta_tracker.animate.increment_value(140))
self.play(te.animate.set_color(RED), run_time=0.5)
# 设定数值
self.play(theta_tracker.animate.set_value(350))
self.wait()
效果如下
%%manim MovingGroupToDestination -ql -v WARNING
class MovingGroupToDestination(Scene):
def construct(self):
# 将4个点形成一个虚拟编组
group = VGroup(Dot(LEFT), Dot(ORIGIN), Dot(RIGHT, color=RED), Dot(2 * RIGHT)).scale(1.4)
# 绘制目标点
dest = Dot([4, 3, 0], color=YELLOW)
# 将编组与目标点添加到场景
self.add(group, dest)
# 编写位移动画,从编组的第二个点到目标点
self.play(group.animate.shift(dest.get_center() - group[2].get_center()))
self.wait()
效果如下
%%manim MovingFrameBox -ql -v WARNING
class MovingFrameBox(Scene):
def construct(self):
# 分三部分绘制一个完整的公式
text=MathTex(
"\\frac{d}{dx}f(x)g(x)=","f(x)\\frac{d}{dx}g(x)","+",
"g(x)\\frac{d}{dx}f(x)"
)
# 播放手写动画
self.play(Write(text))
# 绘制外框间距比为 0.1
framebox1 = SurroundingRectangle(text[1], buff = .1)
framebox2 = SurroundingRectangle(text[3], buff = .1)
# 绘制外框显示动画
self.play(
Create(framebox1),
)
self.wait()
# 绘制第一个框移动并替换第二个框的动画
self.play(
ReplacementTransform(framebox1,framebox2),
)
self.wait()
效果如下
%%manim RotationUpdater -ql -v WARNING
class RotationUpdater(Scene):
def construct(self):
# 创建前向旋转函数,此函数会根据时间dt(deltatime)来决定动画的幅度而不是手工设定
def updater_forth(mobj, dt):
mobj.rotate_about_origin(dt)
# 创建后向旋转函数
def updater_back(mobj, dt):
mobj.rotate_about_origin(-dt)
# 创建旋转的参考线段
line_reference = Line(ORIGIN, LEFT).set_color(WHITE)
# 创建旋转线段
line_moving = Line(ORIGIN, LEFT).set_color(YELLOW)
# 将前向旋转函数加入到线段中
line_moving.add_updater(updater_forth)
# 将两个线段加入到场景中
self.add(line_reference, line_moving)
self.wait(2)
# 移除前向旋转函数
line_moving.remove_updater(updater_forth)
# 将后向向旋转函数加入到线段中
line_moving.add_updater(updater_back)
self.wait(5)
# 移除后向旋转函数
line_moving.remove_updater(updater_back)
self.wait(0.5)
效果如下
%%manim PointWithTrace -ql -v WARNING
class PointWithTrace(Scene):
def construct(self):
# 创建一个空物体
path = VMobject()
# 创建一个点
dot = Dot()
# 定义路径关键点
path.set_points_as_corners([dot.get_center(), dot.get_center()])
# 定义路径更新函数
def update_path(path):
# copy原路径
previous_path = path.copy()
# 增加新的点
previous_path.add_points_as_corners([dot.get_center()])
# 更新当前巨鲸
path.become(previous_path)
#将更新函数加入到路径中
path.add_updater(update_path)
#将路径与点加入到场景中
self.add(path, dot)
# 让dot做圆周运动
self.play(Rotating(dot, radians=PI, about_point=RIGHT, run_time=2))
self.wait()
# 让dot 做线性运动
self.play(dot.animate.shift(UP))
self.play(dot.animate.shift(LEFT))
self.wait()
效果如下
本章主要讲解了maim库中动画的绘制,在下一章中主要介绍2维,3维坐标系、以及摄像机动画的使用