【manim动画教程】--高级动画效果

发布时间 2023-04-14 10:41:31作者: wang_yb

在常用的动画效果中,介绍了一些元素的创建,销毁,移动和变换的方法,这些方法都是针对单个动画的。
如果需要多个动画互相关联,或者元素需要有更复杂的运动方式,那么,仅仅依靠常用的动画效果可能就无法满足要求了。

本篇的高级动画效果主要介绍一些更加灵活的元素运动方式,如何组合多个动画,以及如何联动多个动画的方式。
掌握了这些方法,就能够表达出更加复杂的数学几何动画。

1. 沿着路径运动

常用的动画一般都是直线运动或者圆弧运动,而 MoveAlongPath 可以让元素沿着任意的函数轨迹来运动。
下面的示例是一个点沿着一个较为复杂的三角函数形成的路径运动。

cos_func = FunctionGraph(
    lambda t: np.cos(t) + 0.5 * np.cos(7 * t) + (1 / 7) * np.cos(14 * t),
    color=RED,
)
sc.add(cos_func)
d1 = dot(cos_func.get_start())
sc.play(MoveAlongPath(d1, cos_func), rate_func=linear, run_time=3)

运行效果:
沿着路径运动.gif

2. 保留运动路径

保留动画的轨迹,可以更好的追踪元素的变化规律。
通过 TracedPath可以设置保留运动轨迹的效果。
下面的示例是一个点以一定的弧度运行,并将其运行的轨迹绘制出来。

d = dot(RIGHT * 2)
b = TracedPath(d.get_center)
sc.add(d, b)

sc.play(d.animate(path_arc=PI / 4).shift(LEFT * 2))
sc.play(d.animate(path_arc=-PI / 4).shift(LEFT * 2))

运行效果:
保留运动路径.gif

3. 动画组合

在常用的动画效果中,一般都是逐个显示各个动画效果,manim中提供了几个控制动画组合的方式。
用的较多的两个分别是:

  1. 多个动画组合时,设置每个动画的延迟启动时间
  2. 多个动画组合时,前一个动画成功执行后再执行下一个动画

3.1 延迟启动

通过 LaggedStart控制组合的多个动画之间的启动时间间隔。
下面的示例是一个点先启动,但是要运行1秒才到达终点,
另一个点延迟0.5秒启动,但是运行0.5秒就到达终点,所以两个点最终应该同时到达

d1 = dot(LEFT * 2 + UP, color=RED)
d2 = dot(LEFT * 2 + DOWN)
dl = DashedLine(
    RIGHT * 2 + UP * 3,
    RIGHT * 2 + DOWN * 3,
    dash_length=0.1,
    dashed_ratio=0.8,
    stroke_width=2,
    color=GREEN,
)
sc.add(d1, d2, dl)
sc.wait(0.5)

ani1 = d1.animate(run_time=1).shift(RIGHT * 4)
ani2 = d2.animate(run_time=0.5).shift(RIGHT * 4)

sc.play(LaggedStart(ani1, ani2, lag_ratio=0.5))

运行效果:
动画组合-延迟启动.gif

3.2 顺序启动

通过 Succession控制多个动画顺序执行,它能够保证上一个执行成功后才执行下一个。
下面的示例是4个不同颜色的点,每个点移动成功之后,移动下一个点。

dot1 = Dot(point=LEFT * 2 + UP * 2, radius=0.16, color=BLUE)
dot2 = Dot(point=LEFT * 2 + DOWN * 2, radius=0.16, color=MAROON)
dot3 = Dot(point=RIGHT * 2 + DOWN * 2, radius=0.16, color=GREEN)
dot4 = Dot(point=RIGHT * 2 + UP * 2, radius=0.16, color=YELLOW)
sc.add(dot1, dot2, dot3, dot4)

sc.play(
    Succession(
        dot1.animate.move_to(dot2),
        dot2.animate.move_to(dot3),
        dot3.animate.move_to(dot4),
        dot4.animate.move_to(dot1),
    )
)

运行效果:
动画组合-顺序启动.gif

4. 动画联动

动画联动是比动画组合更复杂的一种模式,动画组合时,每个动画其实是固定的,只是启动的时机不一样。
而在动画联动中,一个动画如何运行是完全基于另一个动画的,而不是实现预设的。

下面的示例是一个角度的联动效果,
初始元素有:

  1. 两条线line1line_moving组成的角度
  2. 标识角度的一段弧线a
  3. 标识角度的字母 tex
  4. 显示角度数值的文字 theta_tex

初始角度 110°,然后可以看到随着 line_moving的移动(先变成 90°,再增加 140°),
atextheta_tex的联动效果。

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)
)

theta_tex = Tex(
    r"$\theta = $",
    str(int(theta_tracker.get_value())),
    r"$^\circ$",
    font_size=50,
    color=RED,
).shift(UP * 2)

sc.add(line1, line_moving, a, tex, theta_tex)
sc.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)
    )
)
theta_tex.add_updater(
    lambda x: x.become(
        Tex(
            r"$\theta = $",
            str(int(theta_tracker.get_value())),
            r"$^\circ$",
            font_size=50,
            color=RED,
        ).shift(UP * 2)
    )
)

sc.play(theta_tracker.animate.set_value(90))
sc.play(theta_tracker.animate.increment_value(140), run_time=3)

运行效果:
动画联动.gif

5. 总结回顾

本篇介绍的高级动画效果,其实是一些更加灵活的制作动画的方法。
利用这些方法,可以更加方便的定制自己的动画效果。

本篇介绍的高级动画效果和上一篇的常用动画效果可以理解成软件的VIP功能和免费功能,
如果免费功能够用,尽量使用免费功能,免费功能满足不了要求的时候,再考虑VIP功能。

本文关联的微信视频号短视频:
manim-高级动画效果-视频号.png