Animations#
There are a wide range of possibilities to animate your mobjects that all work a bit differently. Here is a broad overview so that you can choose the animation stategy that fits best for your project. This chapter will cover ValueTrackers
, Updaters
, self.play Transformations
the mobject.animate
syntax and mobject.become
syntax.
[1]:
from manim import *
config.media_embed = True
Manim Community v0.17.1
[2]:
#ignore this cell, only for setup
param= "-v WARNING --progress_bar None -r 500,200 --disable_caching Example"
NO = Cross(Square(), stroke_color = RED_D, stroke_width = 38).scale(0.9).to_edge(LEFT, buff=1)
YES = SVGMobject("good.svg").to_edge(LEFT, buff=1)
BEST = YES.copy()
BEST.add(Star(color= YELLOW, fill_opacity=1).scale(0.5).move_to(BEST).shift(0.5*DOWN+0.5*RIGHT));
Simple Replacements#
[3]:
%%manim $param
class Example(Scene):
def construct(self):
dot= Dot(color= YELLOW, radius=0.5)
self.add(dot)
self.wait()
dot.scale(2)
self.wait()
dot.scale(2)
self.wait(2)
[4]:
%%manim $param
class Example(Scene):
def construct(self):
dot= Dot(color= YELLOW, radius=0.5)
square= Square(side_length=4,color= BLUE, fill_opacity=1)
triangle= Triangle(radius=3,color= ORANGE, fill_opacity=1).shift(DOWN*0.5)
self.add(dot)
self.wait()
dot.become(square)
self.wait()
dot.become(triangle)
self.wait()
Using .animate Syntax#
[5]:
%%manim $param
class Example(Scene):
def construct(self):
dot= Dot(color= YELLOW, radius=0.5)
self.play(dot.animate.scale(2))
[6]:
%%manim $param
class Example(Scene):
def construct(self):
dot= Dot(color= YELLOW, radius=0.5)
self.play(dot.animate.shift(2*RIGHT))
[7]:
%%manim $param
class Example(Scene):
def construct(self):
dot= Dot(color= YELLOW, radius=0.5)
self.play(dot.animate.set_color(BLUE))
[8]:
%%manim $param
class Example(Scene):
def construct(self):
dot= Dot(color= YELLOW, radius=0.5)
self.play(dot.animate.shift(2*RIGHT).scale(2))
[9]:
%%manim $param
class Example(Scene):
def construct(self):
dot= Dot(color= YELLOW, radius=0.5)
self.play(dot.animate.shift(2*RIGHT).scale(2).set_color(BLUE))
Updaters#
They are very diverse! And they can be used with and without a “dt” parameter
[10]:
%%manim $param
class Example(Scene):
def construct(self):
dot = Dot(color= GREEN, radius=0.7)
self.add(dot)
def foo(mob,dt):
mob.shift(2*RIGHT*dt)
dot.add_updater(foo)
self.wait(3)
[11]:
%%manim $param
class Example(Scene):
def construct(self):
dot = Dot(color= GREEN, radius=0.7)
self.add(dot)
dot.add_updater(lambda x,dt: x.shift(2*RIGHT*dt))
self.wait(3)
[12]:
%%manim $param
class Example(Scene): # when there is no dt parameter, the updater does not work
def construct(self):
dot = Dot(color= GREEN, radius=0.7)
self.add(dot,NO)
dot.add_updater(lambda x : x.shift(2*RIGHT*0.1))
self.wait(3)
Note: Not using the “dt” parameter will make your animation framerate dependent, but this can be solved using ValueTracker, which can be seen in the next section
Updaters + ValueTrackers#
[13]:
%%manim $param
class Example(Scene):
def construct(self):
tracker= ValueTracker(0)
dot = Dot(color= GREEN, radius=0.7)
self.add(dot)
def foo(mob):
mob.move_to(RIGHT*tracker.get_value())
dot.add_updater(foo)
self.play(tracker.animate.set_value(2), rate_func= linear)
Note: now you can also use rate functions:
[14]:
%%manim $param
class Example(Scene):
def construct(self):
tracker= ValueTracker(0)
dot = Dot(color= GREEN, radius=0.7)
self.add(dot)
def foo(mob):
mob.move_to(RIGHT*tracker.get_value())
dot.add_updater(foo)
self.play(tracker.animate.set_value(2), rate_func= smooth)
and this is how to make a custom rate function, e.g. a for a low frame rate within a high frame rate video:
[15]:
%%manim $param
def low_frame_rate(t):
return np.floor(t*10)/10
class Example(Scene):
def construct(self):
self.camera.background_color = "#ece6e2"
banner = ManimBanner(dark_theme=False).scale(0.7)
banner2 = banner.copy()
Group(banner, banner2).arrange(DOWN)
self.play(banner.expand(), rate_func = low_frame_rate)
self.play(banner2.expand())
self.wait()
[16]:
%%manim $param
class Example(Scene):
def construct(self):
tracker= ValueTracker(0.5)
dot = Dot(color= GREEN, radius=0.7)
self.add(dot)
def foo(mob):
mob.move_to(RIGHT*tracker.get_value())
dot.add_updater(foo)
self.play(tracker.animate.set_value(2.2), rate_func= smooth)
self.play(tracker.animate.increment_value(1), rate_func= smooth)
self.play(tracker.animate.increment_value(-1), rate_func= smooth)
self.play(tracker.animate.set_value(0.5), rate_func= linear)
It’s also possible to use different rate functions simultaneously:
[17]:
%%manim $param
class Example(Scene):
def construct(self):
line1 = Line(3*LEFT, 3*RIGHT, stroke_width=10).set_color(RED)
line2 = Line(3*LEFT, 3*RIGHT, stroke_width=10).set_color(GREEN)
Group(line1,line2).arrange(DOWN, buff=2)
d1 = Dot(point = line1.get_left(), radius=0.5)
d2 = Dot(point = line2.get_left(), radius=0.5)
label1 = Tex("smooth").scale(2).next_to(line1, buff= 1)
label2 = Tex("linear").scale(2).next_to(line2, buff= 1)
tr1=ValueTracker(-3)
tr2=ValueTracker(-3)
d1.add_updater(lambda z: z.set_x(tr1.get_value()))
d2.add_updater(lambda z: z.set_x(tr2.get_value()))
self.add(line1,line2,d1,d2,label1,label2 )
self.play(tr1.animate(rate_func=smooth).set_value(3), tr2.animate(rate_func=linear).set_value(3))
self.wait()
[18]:
%%manim $param
#one can now also add additional properties to mobjects, in this case a counter.
class Example(Scene):
def construct(self):
tracker= ValueTracker(0)
dot = Dot(color= GREEN, radius=0.7)
self.add(dot)
dot.counter=0
def foo(mob):
mob.move_to(RIGHT*tracker.get_value())
if mob.counter == 20:
mob.set_color(random_bright_color())
mob.counter = 0
mob.counter += 1
dot.add_updater(foo)
self.play(tracker.animate.set_value(2), rate_func= linear, run_time=3)
Transformations#
[19]:
%%manim $param
class Example(Scene):
def construct(self):
d= Dot(color= YELLOW, radius=0.5)
d2= d.copy().shift(2*RIGHT)
self.play(Transform(d, d2))
Does and Donts#
Note that when you choose to work with updaters, your script might depend on the frame rate.
[20]:
%%manim $param
class Example(Scene):
def construct(self):
print(f"{config.frame_rate = }fps")
dotred= Dot(color= RED, radius=0.5).shift(UP)
dotgreen = Dot(color= GREEN, radius=0.5)
dotgreen.next_to(dotred,DOWN)
self.add(dotgreen,dotred)
DIR= 2*RIGHT
dotgreen.add_updater(lambda x,dt: x.shift(DIR*dt))
dotred.add_updater(lambda x,dt: x.shift(DIR*1/60))
self.wait(3)
config.frame_rate = 60fps
[21]:
param5fps = "-v WARNING --progress_bar None --frame_rate=5 -r 500,200 --disable_caching Example"
[22]:
%%manim $param5fps
class Example(Scene):
def construct(self):
print(f"{config.frame_rate = }fps")
dotred= Dot(color= RED, radius=0.5).shift(UP)
dotgreen = Dot(color= GREEN, radius=0.5)
dotgreen.next_to(dotred,DOWN)
self.add(dotgreen,dotred)
DIR= 2*RIGHT
dotgreen.add_updater(lambda x,dt: x.shift(DIR*dt))
dotred.add_updater(lambda x,dt: x.shift(DIR*1/60))
self.wait(3)
config.frame_rate = 5.0fps
Rotation animation#
There are multiple ways to rotate a square, but not all will result in that animation that you might have expected.
[23]:
%%manim $param
class Example(Scene):
def construct(self, **kwargs):
s1= Square().set_color(YELLOW)
self.add(s1, BEST)
self.play(Rotate(s1, angle=PI/2))
[24]:
%%manim $param
class Example(Scene):
def construct(self, **kwargs):
s2= Square().set_color(PURPLE)
self.add(s2, NO)
self.play(s2.animate.rotate(PI/2))
[25]:
%%manim $param
class Example(Scene):
def construct(self, **kwargs):
theta_track= ValueTracker(0)
s3= Square().set_color(ORANGE)
self.add(s3, YES)
s3.previous_angle=0
def pref(x):
x.previous_angle=theta_track.get_value()
s3.add_updater(lambda x: x.rotate(theta_track.get_value()-s3.previous_angle))
s3.add_updater(pref)
self.play(theta_track.animate.increment_value(PI/2))
[26]:
#not yet implemented
#class Example(Scene):
# def construct(self, **kwargs):
#s3b= Square().set_color(YELLOW)
#self.add(s3b)
#theta_track= DeltaValueTracker(0)
#s3b.add_updater(lambda x: x.rotate(theta_track.get_delta_value()))
#self.play(theta_track.animate.set_value(90*DEGREES))
[27]:
%%manim $param
# NOT WORKING!, BAD PRACTICE.
class Example(Scene):
def construct(self, **kwargs):
s4= Square().set_color(GREEN)
self.add(s4, NO)
theta_track= ValueTracker(0)
s4.add_updater(lambda x: x.rotate(theta_track.get_value()))
self.play(theta_track.animate.increment_value(PI/2))
[28]:
%%manim $param
class Example(Scene):
def construct(self, **kwargs):
s6= Square().set_color(PINK)
self.add(s6, YES)
s6.add_updater(lambda x, dt: x.rotate(dt*PI/2))
self.wait(1)
Known bugs#
Bug with updaters that do not have a dt#
[29]:
%%manim $param
class Example(Scene):
def construct(self):
dot = Dot(color= GREEN, radius=0.7)
self.add(dot,NO)
#dot.add_updater(lambda x,dt : x)
dot.add_updater(lambda x : x.shift(2*RIGHT*1/config.frame_rate))
self.wait(3)
[30]:
%%manim $param
class Example(Scene):
def construct(self):
dot = Dot(color= GREEN, radius=0.7)
self.add(dot,YES)
dot.add_updater(lambda x,dt : x) #adding this line will make the updater continiously watch
dot.add_updater(lambda x : x.shift(2*RIGHT*1/config.frame_rate))
self.wait(3)
Bugs with updater in ZoomedScene#
[31]:
%%manim $param
class Example(ZoomedScene):
def __init__(self, **kwargs):
ZoomedScene.__init__(
self,
zoom_factor=0.3,
zoomed_display_height=4,
zoomed_display_width=4,
image_frame_stroke_width=20,
zoomed_camera_config={
"default_frame_stroke_width": 3,
},
**kwargs
)
def construct(self):
d= Dot()
self.add(d)
imgo =Square().scale(0.3).set_color(RED)
self.add(imgo)
#imgo.add_updater(lambda x: x) # COMMENTED OUT
self.activate_zooming(animate=True)
self.play(self.zoomed_camera.frame.animate.shift(0.5 * (LEFT+UP*0.8)))
self.play(self.zoomed_camera.frame.animate.shift(0.5 * (RIGHT+DOWN*2.8)))
[32]:
%%manim $param
class Example(ZoomedScene):
def __init__(self, **kwargs):
ZoomedScene.__init__(
self,
zoom_factor=0.3,
zoomed_display_height=4,
zoomed_display_width=4,
image_frame_stroke_width=20,
zoomed_camera_config={
"default_frame_stroke_width": 3,
},
**kwargs
)
def construct(self):
d= Dot()
self.add(d)
imgo =Square().scale(0.3).set_color(RED)
self.add(imgo)
imgo.add_updater(lambda x: x) # INCLUDED
self.activate_zooming(animate=True)
self.play(self.zoomed_camera.frame.animate.shift(0.5 * (LEFT+UP*0.8)))
self.play(self.zoomed_camera.frame.animate.shift(0.5 * (RIGHT+DOWN*2.8)))