前端时间做尺规作图相关的动画的时候,封装了一个圆规的动画,顺便研究了下 manim
库的动画函数。
manim
本身就是做动画的库,所以,基于它封装自定义的动画非常方便。
对于单个的元素,manim
本身就提供了非常多的动画函数。
比如:创建/消除的动画,移动元素的动画,旋转元素的动画等等,具体可以参考: Animations
如果是做一些简单的演示视频的话,这些内置的动画函数满足要求绰绰有余。
但是,对于多个元素联动的动画,则需要编写各个元素之间的联动规则,来封装符合要求的动画。
下面以圆规动画为例,演示多元素动画如何封装。
圆规动画目的还是画出一个 圆弧
,只是在绘制的圆弧的过程中展示了圆弧的起点,终点以及连接起点终点之间的线。
这样,实际使用圆规作图的时候,可以更好的理解 圆弧
是如何画出来的。
函数的签名如下:
def ruler(sc: Scene, p1, p2, angle=PI, axis=OUT):
"""
圆规动画
Parameters
---------
sc
绘制动画的场景
p1
代表圆规的针,绘制时不动的点
p2
代表圆规的笔芯,绘制圆弧的点
angle
绘制圆弧的角度,默认PI,相当于绘制半个圆
axis
只有2个值 IN/OUT,分别表示顺时针还是逆时针作弧
"""
# 省略。。。
return arc
各个参数的含义参见注释。
实现动画的思路如下:
d1
(根据参数中p1坐标绘制的点),d2
(根据参数中p2坐标绘制的点)以及 dl
(连接p1和p2的虚线)dl
的动画,随着 d1
和d2
变化不断重新绘制(这里 d1
其实是不会变的)d2
的变动,不断绘制新的圆弧manim
自带的动画函数让 d2
先动,其他动画则会随之一起sc
)中def ruler(sc: Scene, p1, p2, angle=PI, axis=OUT):
"""
圆规动画
Parameters
---------
sc
绘制动画的场景
p1
代表圆规的针,绘制时不动的点
p2
代表圆规的笔芯,绘制圆弧的点
angle
绘制圆弧的角度,默认PI,相当于绘制半个圆
axis
只有2个值 IN/OUT,分别表示顺时针还是逆时针作弧
"""
d1 = Dot(point=p1, color=RED)
d2 = Dot(point=p2, color=GREEN)
dl = DashedLine(d1.get_center(), d2.get_center())
r = np.linalg.norm(p2 - p1)
arc = ArcBetweenPoints(p2, p2)
dl.add_updater(lambda z: z.become(DashedLine(d1.get_center(), d2.get_center())))
if np.array_equal(axis, OUT):
arc.add_updater(
lambda z: z.become(
ArcBetweenPoints(p2, d2.get_center(), radius=r, stroke_color=GREEN)
)
)
if np.array_equal(axis, IN):
arc.add_updater(
lambda z: z.become(
ArcBetweenPoints(d2.get_center(), p2, radius=r, stroke_color=GREEN)
)
)
sc.add(d1, d2, dl, arc)
sc.play(
Rotate(
d2,
about_point=d1.get_center(),
axis=axis,
angle=angle,
rate_func=linear,
)
)
arc.clear_updaters()
dl.clear_updaters()
sc.remove(d1, d2, dl)
return arc
封装之后,使用起来非常简单:
# -*- coding: utf-8 -*-
from manim import *
import numpy as np
class Sample(Scene):
def construct(self):
ruler(self, np.array([-1, 0, 0]), np.array([-1, 1, 0]))
ruler(self, np.array([1, 0, 0]), np.array([1, 1, 0]), axis=IN)
self.wait()
上面演示了逆时针(默认是逆时针)和顺时针方式绘制半圆。
各个元素的颜色等相关属性,没有暴露到函数参数中,可以直接在代码中修改。
这里主要演示如何基于 manim
制作多元素动画的。