动画系统¶
到目前为止,你已经见到了两种动画创建方式:
通过
物件.anim创建组件插值动画通过
动画名(物件, 动画参数)为物件应用内置的特殊动画效果
circle = Circle()
tri = Triangle()
self.forward()
self.play(Create(circle))
self.play(circle.anim.points.shift(LEFT * 3).scale(1.5))
self.play(circle.anim.set(color=RED, fill_alpha=0.5))
self.play(SpinInFromNothing(tri))
self.play(tri.anim.points.shift(RIGHT * 3).scale(1.5))
self.play(tri.anim.set(color=BLUE, fill_alpha=0.5))
self.forward()
基础动画参数¶
无论是哪种创建动画的方式,它们都有几个关键的参数
duration: 动画持续时间大多数动画的默认时长是 1 秒,如果你需要更长或者更短的时间可以额外设置
at: 动画开始的时间点意味着动画会在当前时刻的多少秒之后才开始,比如
at=1, duration=2意味着动画会在当前时刻的 1 秒后开始,进行 2 秒这个参数在接下来将要讲的“动画组”的内容中,会更加实用
rate_func: 动画的缓动函数大多数动画默认以
smooth()的方式进行插值,使得动画过程在开始和结束的时候较慢,中间过程较快,总体上表现为一个平滑的过渡其它一些常用的缓动函数还有
linear(),使得动画全程匀速进行; 以及rush_into()rush_from()等,具体请参考 rate_functions 中的介绍
上面这些参数可以调整动画的表现细节,例如 duration 可以调整动画快慢,控制节奏, at 可以控制动画的开始时机;
当你想要让物体快速进入并逐渐减速时,可以考虑使用 rush_into() 作为缓动函数;总之,可以多多地探索这些参数的使用,获得更好的动画效果。
以下是对上面的动画参数进行一些调整后的示例:
circle = Circle()
star = Star()
self.forward()
self.play(Create(circle, duration=0.8, rate_func=rush_from))
self.play(
circle.anim(rate_func=ease_out_bounce)
.points.shift(LEFT * 3).scale(1.5)
)
self.play(
circle.anim(duration=2)
.set(color=RED, fill_alpha=0.5)
)
self.play(SpinInFromNothing(star, duration=0.6, rate_func=rush_from))
self.play(
star.anim(rate_func=ease_out_bounce)
.points.shift(RIGHT * 3).scale(1.5)
)
self.play(
star.anim(duration=2)
.set(color=YELLOW, fill_alpha=0.5)
)
self.forward()
将
Create和SpinInFromNothing的缓动函数改为了rush_from(),并缩短它们进入的时长将移动动画的缓动函数改为了
ease_out_bounce(),使得物体在移动终点处有一个弹跳的效果将变色动画的时长改为了 2 秒
小技巧
在 .anim 后紧跟括号填入参数即可改变其动画参数。
在合适的地方增加换行可以优化代码的可读性,特别是在动画调用较长的时候。
动画组¶
动画并不是只能像上面一样单独依次执行,我们还可以让多个动画一起执行,创建更加丰富的动画效果。
首先是最基础的, 放在同一个 self.play 函数中的动画会一起执行,你也可以给动画分别传入 at 参数来控制它们的开始时机:
circle = Circle()
circle.points.to_border(UL, buff=LARGE_BUFF)
square = Square()
square.points.to_border(DL, buff=LARGE_BUFF)
self.play(
FadeIn(circle),
FadeIn(square)
)
self.play(
circle.anim
.points.to_border(UR, buff=LARGE_BUFF),
square.anim(at=0.2)
.points.to_border(DR, buff=LARGE_BUFF)
)
你还可以使用 AnimGroup Succession 等方式来组合多个动画。
其中
AnimGroup只是单纯地将多个动画组合到一起,可以统一应用at和duration等参数AnimGroup会根据传入的duration参数将内部动画结构进行整体伸缩以匹配时长Succession则会将多个动画串联起来,前一个动画结束后再开始下一个动画
circle = Circle()
circle.points.to_border(UL, buff=LARGE_BUFF)
square = Square()
square.points.to_border(DL, buff=LARGE_BUFF)
self.play(
FadeIn(circle),
FadeIn(square)
)
self.play(
Succession(
circle.anim(rate_func=rush_into)
.points.to_border(UR, buff=LARGE_BUFF),
square.anim(rate_func=rush_from)
.points.to_border(DR, buff=LARGE_BUFF),
duration=3
),
AnimGroup(
ShowCreationThenDestructionAround(circle),
ShowCreationThenDestructionAround(square),
at=0.5,
duration=2
)
)
提示
其实 self.play 函数本身就充当一个 AnimGroup 的角色,
所以你可以直接将多个动画放在 self.play 中,并应用 at 和 duration 等参数。
备注
关于动画组的更多内容,可以参考 composition 中的介绍,
其中还提及了关于 lag_ratio 和 offset 参数的使用,这里不再展开叙述。
预先设置动画¶
当我们使用 self.play 播放一个长达 4 秒的动画之后,当前时刻便会跳转至 4 秒后,
但是我们就失去了在这 4 秒内创建其它动画的机会,因为只能往前走而不能倒退。
因此,JAnim 提供了一个实用的功能——预先设置动画,但不在时间上前进,可以调用 self.prepare 做到:
txt = Text('JAnim')
txt.points.shift(LEFT * 2)
self.prepare(
CircleIndicate(txt),
at=1,
duration=2
)
self.play(txt.anim.points.shift(RIGHT * 4).scale(2), duration=2)
self.play(txt.anim.points.shift(LEFT * 4).scale(0.5), duration=2)
在该示例中,我们使用 self.prepare 预先设置了一个 CircleIndicate 动画,
使得在文字在后续的移动动画中,能够在预先设置的时间段看到黄圈高亮的效果。
备注
从原理上来讲,其实 play 就是 prepare + forward 的组合。
动画序列控制¶
在 Succession 的动画序列中,我们可以插入一些额外的控制,例如:
dot = Dot(RIGHT * 2).show()
txt = Text('just a dot').show()
txt.points.next_to(dot, DOWN)
star = Star(start_angle=0, outer_radius=2)
star.points.shift(dot.points.box.center - star.points.get()[0])
txt1 = Text('Rotating...', font_size=60, color=GREY_D, depth=1)
txt2 = Text('Drawing a star!', font_size=60, color=GREY_D, depth=1)
self.forward()
self.play(
Aligned(
Succession(
Do(txt1.show),
Rotate(dot, TAU, about_point=ORIGIN, duration=2),
Do(txt1.hide),
Wait(0.5),
Do(txt2.show),
AnimGroup(
MoveAlongPath(dot, star),
Create(star, auto_close_path=False),
duration=2
),
Do(txt2.hide)
),
Follow(txt, dot, DOWN)
)
)
self.forward()
内置动画¶
关于更多可用的内置动画,可查阅以下列表中的内容:
JAnim 还有一个重要的特性是“动画复合”,我们将在 Updater 的使用 中详细介绍这一特性。
.r 的使用¶
在 JAnim 中,由于 物件-组件 的结构关系,导致在一个组件中进行完操作后,
需要使用 .r 来返回物件级别,从而再访问物件或是其它组件中的功能,例如:
item.points.shift(LEFT * 2).r.color.fade(0.5)
或是对于动画而言
self.play(
item.anim.points.shift(LEFT * 2).r.color.fade(0.5)
)