跳转至

同时与依次

影片代码
import manimgx as m


class TogetherHero(m.Scene):
    def construct(self) -> None:
        dots = m.VGroup(*[m.Dot(radius=0.2, color=m.YELLOW) for _ in range(6)]).arrange(
            buff=0.8
        )
        self.play(
            m.LaggedStart(*[m.GrowFromCenter(dot) for dot in dots], lag_ratio=0.3)
        )
        self.play(m.Succession(*[dot.animate.shift(m.UP) for dot in dots], run_time=2))
        self.play(m.AnimationGroup(*[dot.animate.shift(m.DOWN) for dot in dots]))
        self.wait()

self.play(a, b) 同时播放 a 和 b。组将动画作为一个整体播放:同时、依次,或每个在前 一个之后稍后播放。组本身也是一个动画,因此组可以嵌套,组也可以在更大的播放中播放。

AnimationGroup

代码
import manimgx as m


class AnimationGroupExample(m.Scene):
    def construct(self) -> None:
        shapes = (
            m.VGroup(
                m.Square(color=m.BLUE, fill_opacity=0.5),
                m.Circle(color=m.YELLOW, fill_opacity=0.5),
                m.Triangle(color=m.GREEN, fill_opacity=0.5),
            )
            .scale(1.2)
            .arrange(buff=1)
        )
        self.play(
            m.AnimationGroup(
                m.Create(shapes[0]),
                m.FadeIn(shapes[1], shift=m.UP),
                m.GrowFromCenter(shapes[2]),
                lag_ratio=0.5,
            )
        )

一起播放多个动画,每个在组时间的自己的窗口中。

各部分在时间中布局:每个部分在其前一个部分播放了 lag_ratio 的运行时长后开始(默认 0 全部同时,1 一个接一个, 介于其间则重叠),并播放其自身的运行时长。组持续到其 最后一个部分结束,除非给定 run_time,它会将整个 布局拉伸或压缩以适应。其速率函数(默认 linear)扭曲组的时间,而 每个部分仍以其自身的速率函数缓急其自己的窗口。

一个部分在其窗口打开时开始,按其 mobject 当时的状态接管它们(并 在其引入时将其 mobject 带入场景),在其窗口关闭时完成,无论其他 仍在播放什么,按其离开方式离开场景(remover 的 mobject 离开它)。一个其窗口 与组的结束一同结束的部分与组一同完成,在组的速率函数结束其时间之处。 在其窗口之前部分未触及任何内容;在其之后,其所做的保持完成。组本身不是场景的 mobject:它不添加自己的任何内容。Scene.play 将 多个动画作为这样的组播放。

m.AnimationGroup(*animations, group=None, **kwargs)
*animations

各动画,或它们的可迭代对象。

group

组所动画的 mobject;默认为其各部分的 mobject 的组,但那些部分引入的除外。

run_time

动画播放时长,以秒为单位(默认 1)。

lag_ratio

mobject 各部分如何交错:每个部分在其前一个部分开始后,经过其运行时长的该分数比例后开始 (默认 0:全部同时;1:一个接一个)。

rate_func

动画进度如何随时间运行:一个从 [0, 1] 到 [0, 1] 的函数 (默认为 smooth;参见 rate functions)。

reverse_rate_function

是否反向运行动画(默认 False)。

name

动画的名称。

remover

动画结束时 mobject 是否离开场景 (默认 False)。

suspend_mobject_updating

动画期间 mobject 的更新器是否在其下运行(默认 True):它们 持续作用于 mobject,每一帧显示动画应用于其结果。 若为 False,则它们作用于被动画的 mobject 本身。

introducer

动画开始时 mobject 是否加入场景(默认 False); 否则若场景缺少它,play 在其开始时将其带入。

use_override

若某 mobject 的类用另一个动画代替此动画播放,是否如此执行 (默认 True)。

还接受 动画关键字。

源码

manimgx\animation\timeline.py

def __init__(
    self,
    *animations: Animation
    | Iterable[Animation],  # of any mobjects (Animation is invariant in its)
    group: Group | VGroup | None = None,
    **kwargs: Unpack[AnimationOptions],
):
    self.animations = [prepare(a) for a in _flatten(animations)]
    self.group = group or Group(
        *remove_list_redundancies(
            [a.mobject for a in self.animations if not a.is_introducer()]
        )
    )
    super().__init__(self.group, **kwargs)
    self.scene: Scene | None = None
    self.begun: set[int] = set()
    self.done: set[int] = set()
    self._fresh: set[int] = set()  # the parts begun at the instant being computed
    # a run time given; None: the layout's own, where its last part ends
    self._given: float | Fraction | None = (
        animation_defaults(type(self)) | kwargs
    ).get("run_time")
    self._layout: tuple[tuple[object, ...], Fraction, list[Placed]] = ((), _0, [])
    self._playing = False  # between `begin` and `finish`: its layout fixed
    self._placed: tuple[tuple[object, ...], list[Placed]] = ((), [])

run_time

组播放时长,以秒为单位:给定的运行时长,否则为其 最后一个部分结束的位置。

animation_group.run_time
源码

manimgx\animation\timeline.py

def run_time(self) -> float:
    """组播放时长,以秒为单位:给定的运行时长,否则为其
    最后一个部分结束的位置。"""
    return self.max_end_time if self._given is None else float(self._given)

Succession

代码
import manimgx as m


class SuccessionExample(m.Scene):
    def construct(self) -> None:
        square = m.Square(side_length=2, color=m.BLUE, fill_opacity=0.5)
        square.shift(3 * m.LEFT)
        self.play(
            m.Succession(
                m.Create(square),
                square.animate.shift(6 * m.RIGHT),
                m.Rotate(square, m.PI / 4),
                square.animate.set_color(m.YELLOW),
            )
        )

一个接一个地播放动画。

一个 lag_ratio 为 1 的 AnimationGroup:每个部分 在其前一个结束时开始,从前一个留下的状态继续。

m.Succession(*animations, group=None, **kwargs)
*animations

各动画,按顺序,或它们的可迭代对象。

group

组所动画的 mobject;默认为其各部分的 mobject 的组,但那些部分引入的除外。

run_time

动画播放时长,以秒为单位(默认 1)。

lag_ratio

mobject 各部分如何交错:每个部分在其前一个部分开始后,经过其运行时长的该分数比例后开始 (默认 0:全部同时;1:一个接一个)。

rate_func

动画进度如何随时间运行:一个从 [0, 1] 到 [0, 1] 的函数 (默认为 smooth;参见 rate functions)。

reverse_rate_function

是否反向运行动画(默认 False)。

name

动画的名称。

remover

动画结束时 mobject 是否离开场景 (默认 False)。

suspend_mobject_updating

动画期间 mobject 的更新器是否在其下运行(默认 True):它们 持续作用于 mobject,每一帧显示动画应用于其结果。 若为 False,则它们作用于被动画的 mobject 本身。

introducer

动画开始时 mobject 是否加入场景(默认 False); 否则若场景缺少它,play 在其开始时将其带入。

use_override

若某 mobject 的类用另一个动画代替此动画播放,是否如此执行 (默认 True)。

还接受 动画关键字。

源码

manimgx\animation\timeline.py

def __init__(
    self,
    *animations: Animation
    | Iterable[Animation],  # of any mobjects (Animation is invariant in its)
    group: Group | VGroup | None = None,
    **kwargs: Unpack[AnimationOptions],
):
    self.animations = [prepare(a) for a in _flatten(animations)]
    self.group = group or Group(
        *remove_list_redundancies(
            [a.mobject for a in self.animations if not a.is_introducer()]
        )
    )
    super().__init__(self.group, **kwargs)
    self.scene: Scene | None = None
    self.begun: set[int] = set()
    self.done: set[int] = set()
    self._fresh: set[int] = set()  # the parts begun at the instant being computed
    # a run time given; None: the layout's own, where its last part ends
    self._given: float | Fraction | None = (
        animation_defaults(type(self)) | kwargs
    ).get("run_time")
    self._layout: tuple[tuple[object, ...], Fraction, list[Placed]] = ((), _0, [])
    self._playing = False  # between `begin` and `finish`: its layout fixed
    self._placed: tuple[tuple[object, ...], list[Placed]] = ((), [])

LaggedStart

代码
import manimgx as m


class LaggedStartExample(m.Scene):
    def construct(self) -> None:
        dots = m.VGroup(*(m.Dot(radius=0.25, color=m.YELLOW) for _ in range(8)))
        dots.arrange(buff=0.8).shift(2 * m.UP)
        self.add(dots)
        self.play(
            m.LaggedStart(
                *(dot.animate.shift(4 * m.DOWN) for dot in dots), lag_ratio=0.2
            )
        )

一个接一个稍后播放动画,相互重叠。

一个 lag_ratio 较小(默认 0.05)的 AnimationGroup: 每个部分在其前一个播放了该分数比例的运行时长后开始。

m.LaggedStart(*animations, group=None, **kwargs)
*animations

各动画,按顺序,或它们的可迭代对象。

group

组所动画的 mobject;默认为其各部分的 mobject 的组,但那些部分引入的除外。

run_time

动画播放时长,以秒为单位(默认 1)。

lag_ratio

mobject 各部分如何交错:每个部分在其前一个部分开始后,经过其运行时长的该分数比例后开始 (默认 0:全部同时;1:一个接一个)。

rate_func

动画进度如何随时间运行:一个从 [0, 1] 到 [0, 1] 的函数 (默认为 smooth;参见 rate functions)。

reverse_rate_function

是否反向运行动画(默认 False)。

name

动画的名称。

remover

动画结束时 mobject 是否离开场景 (默认 False)。

suspend_mobject_updating

动画期间 mobject 的更新器是否在其下运行(默认 True):它们 持续作用于 mobject,每一帧显示动画应用于其结果。 若为 False,则它们作用于被动画的 mobject 本身。

introducer

动画开始时 mobject 是否加入场景(默认 False); 否则若场景缺少它,play 在其开始时将其带入。

use_override

若某 mobject 的类用另一个动画代替此动画播放,是否如此执行 (默认 True)。

还接受 动画关键字。

源码

manimgx\animation\timeline.py

def __init__(
    self,
    *animations: Animation
    | Iterable[Animation],  # of any mobjects (Animation is invariant in its)
    group: Group | VGroup | None = None,
    **kwargs: Unpack[AnimationOptions],
):
    self.animations = [prepare(a) for a in _flatten(animations)]
    self.group = group or Group(
        *remove_list_redundancies(
            [a.mobject for a in self.animations if not a.is_introducer()]
        )
    )
    super().__init__(self.group, **kwargs)
    self.scene: Scene | None = None
    self.begun: set[int] = set()
    self.done: set[int] = set()
    self._fresh: set[int] = set()  # the parts begun at the instant being computed
    # a run time given; None: the layout's own, where its last part ends
    self._given: float | Fraction | None = (
        animation_defaults(type(self)) | kwargs
    ).get("run_time")
    self._layout: tuple[tuple[object, ...], Fraction, list[Placed]] = ((), _0, [])
    self._playing = False  # between `begin` and `finish`: its layout fixed
    self._placed: tuple[tuple[object, ...], list[Placed]] = ((), [])

LaggedStartMap

代码
import manimgx as m


class LaggedStartMapExample(m.Scene):
    def construct(self) -> None:
        dots = m.VGroup(*(m.Dot(radius=0.2) for _ in range(35)))
        dots.arrange_in_grid(rows=5, cols=7, buff=0.6)
        self.play(m.LaggedStartMap(m.GrowFromCenter, dots, lag_ratio=0.1))

在 mobject 的每个子对象上播放动画,一个接一个稍后开始。

每个子对象的动画为 animation_class(*arg_creator(submobject), **kwargs) (默认为 animation_class(submobject, **kwargs)),它们作为 LaggedStart 播放。run_time(默认 2 秒)和 lag_ratio 为整体计时;其他所有选项传给每个动画。

m.LaggedStartMap(animation_class, mobject, arg_creator=None, **kwargs)
animation_class

在每个子对象上播放的动画:一个类,或任何 返回动画的函数。

mobject

其子对象被动画的 mobject。

arg_creator

从子对象到动画位置参数的函数;None 表示 仅子对象本身。

run_time

动画播放时长,以秒为单位(默认 1)。

lag_ratio

mobject 各部分如何交错:每个部分在其前一个部分开始后,经过其运行时长的该分数比例后开始 (默认 0:全部同时;1:一个接一个)。

rate_func

动画进度如何随时间运行:一个从 [0, 1] 到 [0, 1] 的函数 (默认为 smooth;参见 rate functions)。

reverse_rate_function

是否反向运行动画(默认 False)。

name

动画的名称。

remover

动画结束时 mobject 是否离开场景 (默认 False)。

suspend_mobject_updating

动画期间 mobject 的更新器是否在其下运行(默认 True):它们 持续作用于 mobject,每一帧显示动画应用于其结果。 若为 False,则它们作用于被动画的 mobject 本身。

introducer

动画开始时 mobject 是否加入场景(默认 False); 否则若场景缺少它,play 在其开始时将其带入。

use_override

若某 mobject 的类用另一个动画代替此动画播放,是否如此执行 (默认 True)。

还接受 动画关键字。

源码

manimgx\animation\timeline.py

def __init__(
    self,
    animation_class: Callable[
        ..., Animation
    ],  # given each part's arguments (`arg_creator`)
    mobject: Mobject,
    arg_creator: Callable[[Mobject], Iterable[object]] | None = None,
    **kwargs: Unpack[AnimationOptions],
):
    timing: AnimationOptions = {}
    if "run_time" in kwargs:
        timing["run_time"] = kwargs.pop("run_time")
    if "lag_ratio" in kwargs:
        timing["lag_ratio"] = kwargs.pop("lag_ratio")
    arguments = arg_creator or (lambda part: (part,))
    super().__init__(
        *(animation_class(*arguments(part), **kwargs) for part in mobject), **timing
    )

ChangeSpeed

代码
import manimgx as m


class ChangeSpeedExample(m.Scene):
    def construct(self) -> None:
        steady = m.Dot(5 * m.LEFT + m.UP, radius=0.25, color=m.BLUE)
        slowed = m.Dot(5 * m.LEFT + m.DOWN, radius=0.25, color=m.YELLOW)
        self.add(steady, slowed)
        self.play(
            steady.animate(run_time=2, rate_func=m.linear).set_x(5),
            m.ChangeSpeed(
                slowed.animate(run_time=2, rate_func=m.linear).set_x(5),
                speedinfo={0.3: 1, 0.4: 0.2, 0.6: 0.2, 0.7: 1},
            ),
        )

沿途中更快或更慢地播放动画:速度随播放而变化。

speedinfo 给出动画进度各点处的速度:{0.5: 2} 在开始时以自身速度播放, 加速到中间时两倍快,此后保持两倍快。两点之间速度稳定变化(在场景时间中 以恒定加速度),因此从进度 a 到 b 的区段,在速度 v 和 w 下, 占用动画运行时长的 (b - a) · 2 / (v + w);播放持续其各区段占用时间之和。除非给定, 起始速度为 1,结束速度为最后一个点的速度。

借助 affects_speed_updaters,用 ChangeSpeed.add_updater 添加的更新器在播放时 以相同速度运行,使得它们所移动的内容与动画保持同步。

速度仅改变动画的计时:它保留自身的速率函数(及 其各部分的交错),因此全程速度为 1 时即为动画本身。

m.ChangeSpeed(anim, speedinfo, affects_speed_updaters=True, **kwargs)
anim

要播放的动画。

speedinfo

动画进度各点处的速度,从 0 到 1:速度 2 表示两倍快播放,0.5 表示一半快。

affects_speed_updaters

用 ChangeSpeed.add_updater 添加的更新器 是否在播放时跟随速度。

run_time

动画播放时长,以秒为单位(默认 1)。

lag_ratio

mobject 各部分如何交错:每个部分在其前一个部分开始后,经过其运行时长的该分数比例后开始 (默认 0:全部同时;1:一个接一个)。

rate_func

动画进度如何随时间运行:一个从 [0, 1] 到 [0, 1] 的函数 (默认为 smooth;参见 rate functions)。

reverse_rate_function

是否反向运行动画(默认 False)。

name

动画的名称。

remover

动画结束时 mobject 是否离开场景 (默认 False)。

suspend_mobject_updating

动画期间 mobject 的更新器是否在其下运行(默认 True):它们 持续作用于 mobject,每一帧显示动画应用于其结果。 若为 False,则它们作用于被动画的 mobject 本身。

introducer

动画开始时 mobject 是否加入场景(默认 False); 否则若场景缺少它,play 在其开始时将其带入。

use_override

若某 mobject 的类用另一个动画代替此动画播放,是否如此执行 (默认 True)。

还接受 动画关键字。

源码

manimgx\animation\timeline.py

def __init__(
    self,
    anim: Animation,
    speedinfo: dict[float, float],
    affects_speed_updaters: bool = True,
    **kwargs: Unpack[AnimationOptions],
) -> None:
    self.anim = prepare(anim)
    speeds = dict(sorted(({0: 1} | speedinfo).items()))
    speeds.setdefault(1, speeds[max(speeds)])
    self.speedinfo = speeds
    self.affects_speed_updaters = affects_speed_updaters
    # each stretch: (from, to, speed at from, speed at to, where it starts in real time)
    self._stretches: list[tuple[float, float, float, float, float]] = []
    total = _0  # real time, per second of the wrapped animation
    for (a, v), (b, w) in pairwise(speeds.items()):
        self._stretches.append((a, b, v, w, float(total)))
        total += (
            2
            * (clock.rational(b) - clock.rational(a))
            / (clock.rational(v) + clock.rational(w))
        )
    self._total = float(total)
    self._before = kwargs.pop("rate_func", linear)
    kwargs["rate_func"] = self._rate
    duration = total * self.anim._duration
    kwargs["run_time"] = float(duration)
    super().__init__(self.anim, **kwargs)
    self._given = duration

add_updater

向 mobject 添加更新器,其时间跟随任何正在播放的 ChangeSpeed 的速度。

以此方式添加的基于时间的更新器,作为 dt 接收一个时钟的时间,该时钟与 场景时钟一同运行,除非带有 affects_speed_updaters 的 ChangeSpeed 正在播放: 此时它以该 ChangeSpeed 的速度运行(其 speedinfo,无论其速率函数如何缓急),当多个同时播放时,以最后开始的那个的 速度运行。该时钟绝不反向运行。它保持其种类:一个 flow 仍是 flow,任何其他基于时间的更新器仍在模拟时钟上步进。逐帧更新器无时间, 按原样添加。

ChangeSpeed.add_updater(mobject, update_function, index=None, call_updater=False)
mobject

要添加到的 mobject。

update_function

更新器:mobject 的函数,或 mobject 和 dt 的函数(参见 add_updater)。

index

其在 mobject 更新器中的位置,更新器按顺序运行;None 表示最后。

call_updater

是否立即运行一次;基于时间的更新器接收 dt 为 0。

源码

manimgx\animation\timeline.py

@classmethod
def add_updater(
    cls,
    mobject: Mobject,
    update_function: Updater[Mobject],
    index: int | None = None,
    call_updater: bool = False,
) -> None:
    """向 mobject 添加更新器,其时间跟随任何正在播放的 `ChangeSpeed` 的速度。

    以此方式添加的基于时间的更新器,作为 `dt` 接收一个时钟的时间,该时钟与
    场景时钟一同运行,除非带有 `affects_speed_updaters` 的 `ChangeSpeed` 正在播放:
    此时它以该 `ChangeSpeed` 的速度运行(其
    `speedinfo`,无论其速率函数如何缓急),当多个同时播放时,以最后开始的那个的
    速度运行。该时钟绝不反向运行。它保持其种类:一个 [flow][manimgx.mobject.flow]
    仍是 flow,任何其他基于时间的更新器仍在模拟时钟上步进。逐帧更新器无时间,
    按原样添加。

    Args:
        mobject: 要添加到的 mobject。
        update_function: 更新器:mobject 的函数,或 mobject 和
            `dt` 的函数(参见 [`add_updater`][manimgx.Mobject.add_updater])。
        index: 其在 mobject 更新器中的位置,更新器按顺序运行;None
            表示最后。
        call_updater: 是否立即运行一次;基于时间的更新器接收
            `dt` 为 0。
    """
    if not _per_frame(update_function):
        on_clock = _OnClock(update_function, clock.speed)
        update_function = on_clock if simulated(update_function) else flow(on_clock)
    mobject.add_updater(update_function, index=index, call_updater=call_updater)

Wait

代码
import manimgx as m


class WaitExample(m.Scene):
    def construct(self) -> None:
        finish = m.Line(3 * m.UP, 3 * m.DOWN, color=m.RED).shift(3 * m.RIGHT)
        dot = m.Dot(4 * m.LEFT, radius=0.3, color=m.YELLOW)
        dot.add_updater(lambda mob, dt: mob.shift(3 * dt * m.RIGHT))
        self.add(finish, dot)
        self.play(m.Wait(10, stop_condition=lambda: dot.get_x() >= 3))

让场景的时间继续流逝,不动画任何内容。

场景的更新器及其 mobject 的更新器在其中持续运行; Scene.wait 播放一个。单独播放时,它可以提前结束,于 stop_condition 处,或冻结帧;在组合中,它仅占用其时间。

m.Wait(run_time=1, stop_condition=None, frozen_frame=None, **kwargs)
run_time

持续时长,以秒为单位。

stop_condition

每一帧检查的函数:等待在它返回 True 的第一帧结束。None:持续其整个运行时长。

frozen_frame

时间是否静止:保持一帧,更新器不 运行,随后如同没有时间流逝般继续。它不能与 stop_condition 组合使用(引发 ValueError)。

lag_ratio

mobject 各部分如何交错:每个部分在其前一个部分开始后,经过其运行时长的该分数比例后开始 (默认 0:全部同时;1:一个接一个)。

rate_func

动画进度如何随时间运行:一个从 [0, 1] 到 [0, 1] 的函数 (默认为 smooth;参见 rate functions)。

reverse_rate_function

是否反向运行动画(默认 False)。

name

动画的名称。

remover

动画结束时 mobject 是否离开场景 (默认 False)。

suspend_mobject_updating

动画期间 mobject 的更新器是否在其下运行(默认 True):它们 持续作用于 mobject,每一帧显示动画应用于其结果。 若为 False,则它们作用于被动画的 mobject 本身。

introducer

动画开始时 mobject 是否加入场景(默认 False); 否则若场景缺少它,play 在其开始时将其带入。

use_override

若某 mobject 的类用另一个动画代替此动画播放,是否如此执行 (默认 True)。

还接受 动画关键字。

源码

manimgx\animation\timeline.py

def __init__(
    self,
    run_time: float = 1,
    stop_condition: Callable[[], bool] | None = None,
    frozen_frame: bool | None = None,
    **kwargs: Unpack[Untimed],
) -> None:
    if stop_condition and frozen_frame:
        raise ValueError("A static Wait animation cannot have a stop condition.")
    self.duration = run_time
    self.stop_condition = stop_condition
    self.is_static_wait = frozen_frame
    super().__init__(None, run_time=run_time, **kwargs)

Add

代码
import manimgx as m


class AddExample(m.Scene):
    def construct(self) -> None:
        words = m.VGroup(
            *(m.Text(word, font_size=96) for word in ("one", "two", "three"))
        ).arrange(m.DOWN, buff=0.5)
        box = m.SurroundingRectangle(words, buff=0.5, color=m.BLUE)
        self.play(
            m.Create(box, run_time=3),
            m.Succession(*(m.Add(word, run_time=1) for word in words)),
        )

在组合的某一时刻将 mobject 加入场景。

它在开始时加入其 mobject,默认不占用时间:在 Succession 中,它们在其之前的动画完成时出现。 给定 run_time 时,它在加入它们后保持那么久。

m.Add(*mobjects, **kwargs)
*mobjects

要添加的 mobject;多个作为一个组添加。

run_time

动画播放时长,以秒为单位(默认 1)。

lag_ratio

mobject 各部分如何交错:每个部分在其前一个部分开始后,经过其运行时长的该分数比例后开始 (默认 0:全部同时;1:一个接一个)。

rate_func

动画进度如何随时间运行:一个从 [0, 1] 到 [0, 1] 的函数 (默认为 smooth;参见 rate functions)。

reverse_rate_function

是否反向运行动画(默认 False)。

name

动画的名称。

remover

动画结束时 mobject 是否离开场景 (默认 False)。

suspend_mobject_updating

动画期间 mobject 的更新器是否在其下运行(默认 True):它们 持续作用于 mobject,每一帧显示动画应用于其结果。 若为 False,则它们作用于被动画的 mobject 本身。

introducer

动画开始时 mobject 是否加入场景(默认 False); 否则若场景缺少它,play 在其开始时将其带入。

use_override

若某 mobject 的类用另一个动画代替此动画播放,是否如此执行 (默认 True)。

还接受 动画关键字。

源码

manimgx\animation\timeline.py

def __init__(self, *mobjects: Mobject, **kwargs: Unpack[AnimationOptions]) -> None:
    super().__init__(
        mobjects[0] if len(mobjects) == 1 else _gathered(*mobjects), **kwargs
    )