transform

备注

使用 Transform 进行不同文字间的变换可能不会有足够好的效果,在使用时请多加斟酌

class janim.anims.transform.Transform(src_item: Item, target_item: Item, *, path_arc: float = 0, path_arc_axis: Vect = array([0., 0., 1.]), path_func: PathFunc | None = None, flatten: bool = False, root_only: bool = False, hide_src: bool = True, show_target: bool = True, src_fade: float = 0, target_fade: float = 0, **kwargs)

基类:Animation

创建从 src_itemtarget_item 的插值动画

作用机制:在开始时隐藏源物件,在动画过程中播放“插值效果”,在结束后显示目标物件

参数:
  • src_item -- 变换的起始物件

  • target_item -- 变换的目标物件

路径与插值

参数:
  • path_arc -- 插值路径的圆弧角度;为 0 时按直线插值

  • path_arc_axis -- 当使用圆弧路径时的旋转轴。

  • path_func -- 自定义路径函数;传入后会覆盖 path_arcpath_arc_axis 的默认路径行为

对齐策略

参数:
  • flatten -- 是否忽略子物件层级并直接按顺序展平对齐;默认 False,要求源与目标子结构可对齐

  • root_only -- 是否仅对根物件进行插值而不递归子物件

显隐与淡入淡出

参数:
  • hide_src -- 动画开始时是否自动隐藏源物件

  • show_target -- 动画结束时是否自动显示目标物件

  • src_fade -- 仅当 hide_src=False 时生效;表示源物件在动画开头淡入的时长比例

  • target_fade -- 表示目标物件在动画末尾淡出的时长比例

src_fadetarget_fade 对半透明物件较为实用,可规避开始/结束时重叠导致的透明度突变


基本示例:

TransformExample
from janim.imports import *

class TransformExample(Timeline):
    def construct(self):
        A = Text('Text-A', font_size=72)
        B = Text('Text-B', font_size=72)
        C = Text('C-Text', font_size=72)

        A.show()
        self.forward()
        self.play(Transform(A, B))
        self.forward()
        self.play(Transform(B, C))
        self.forward()

hide_srcshow_target 参数的演示:

TransformHideShowExample
from janim.imports import *

class TransformHideShowExample(Timeline):
    def construct(self):
        squares = Square(color=GREEN) * 3
        squares.points.arrange(DOWN).shift(LEFT * 3)

        circles = Circle(color=BLUE) * 3
        circles.points.arrange(DOWN).shift(RIGHT * 3)

        txts = Text('default\nhide_src=False\nshow_target=False')
        txts.points.arrange(DOWN, buff=2)

        self.show(txts, squares)
        self.forward()
        self.play(
            Transform(squares[0], circles[0]),
            Transform(squares[1], circles[1], hide_src=False),
            Transform(squares[2], circles[2], show_target=False),
            duration=3
        )
        self.forward()

src_fadetarget_fade 参数的演示:

TransformFadeExample
from janim.imports import *

class TransformFadeExample(Timeline):
    def construct(self):
        squares = Square(color=GREEN, fill_alpha=0.4) * 3
        squares.points.arrange(DOWN).shift(LEFT * 3)

        circles = Circle(color=BLUE, fill_alpha=0.4) * 3
        circles.points.arrange(DOWN).shift(RIGHT * 3)

        txts = Text('fade=0\nfade=0.2\nfade=0.4')
        txts.points.arrange(DOWN, buff=2)

        self.show(txts, squares)
        self.forward()
        self.play(
            Transform(squares[0], circles[0], hide_src=False),
            Transform(squares[1], circles[1], hide_src=False, src_fade=0.2),
            Transform(squares[2], circles[2], hide_src=False, src_fade=0.4),
            duration=3
        )
        self.forward()
        self.play(
            Transform(squares[0], circles[0]),
            Transform(squares[1], circles[1], target_fade=0.2),
            Transform(squares[2], circles[2], target_fade=0.4),
            duration=3
        )
        self.forward()
class janim.anims.transform.MoveToTarget(item: Item, **kwargs)

基类:Transform

详见 generate_target()

MoveToTargetExample
from janim.imports import *

class MoveToTargetExample(Timeline):
    def construct(self):
        txt = Text('A Matrix')
        mat = TypstMatrix([
            [1, 2, 3],
            [4, 5, 6],
            [7, 8, 9]
        ])

        self.play(Write(txt))

        Group(txt.generate_target(), mat).points.arrange(DOWN)

        self.play(
            MoveToTarget(txt),
            FadeIn(mat, UP)
        )
        self.forward()
class janim.anims.transform.TransformInSegments(src: Item, src_segments: Iterable[Iterable[int]] | Iterable[int], target: Item, target_segments: Iterable[Iterable[int]] | Iterable[int] | EllipsisType, *, trs_kwargs: dict = {}, **kwargs)

基类:AnimGroup

依照切片列表进行 srctarget 之间的变换


基本用法

TransformInSegments(a, [[0,3], [5,7]],
                    b, [[1,3], [5,7]])

相当于

AnimGroup(Transform(a[0:3], b[1:3]),
          Transform(a[5:7], b[5:7]))

省略变换目标的切片

使用 ... 表示与变换来源的切片相同

TransformInSegments(a, [[0,3], [5,7]],
                    b, ...)

相当于

TransformInSegments(a, [[0,3], [5,7]],
                    b, [[0,3], [5,7]])

连续切片

TransformInSegments(a, [[0,3], [5,7,9]],
                    b, [[1,3], [4,7], [10,14]])

相当于

TransformInSegments(a, [[0,3], [5,7], [7,9]],
                    b, [[1,3], [4,7], [10,14]])

切片简写

如果总共只有一个切片,可以省略一层嵌套

TransformInSegments(a, [0, 4, 6, 8],
                    b, ...)

相当于

TransformInSegments(a, [[0, 4, 6, 8]],
                    b, ...)

连续切片倒序

倒过来写即可使切片倒序

TransformInSegments(a, [8, 6, 4, 0],
                    b, ...)

相当于

TransformInSegments(a, [[6,8], [4,6], [0,4]],
                    b, ...)

请留意 Python 切片中左闭右开的原则,对于倒序序列 [8, 6, 4, 0] 来说则是左开右闭

TransformInSegmentsExample
from janim.imports import *

class TransformInSegmentsExample(Timeline):
    def construct(self):
        typ1 = TypstMath('sin x + cos x')
        typ2 = TypstMath('cos y + sin y')
        typ2.match_pattern(typ1, '+')
        Group(typ1, typ2).points.scale(3)

        self.show(typ1)
        self.forward(0.5)
        self.play(TransformInSegments(typ1, [[0,3,4], [5,8,9]],
                                      typ2, ...,
                                      lag_ratio=0.5))
        self.forward(0.5)
class janim.anims.transform.MethodTransform(item: Item, obj: Item | _AsTypeWrapper, show_at_begin: bool = True, hide_at_end: bool = False, **kwargs)

基类:Transform

依据物件的变换而创建的补间过程

具体参考 anim()

MethodTransformExample
from janim.imports import *

class MethodTransformExample(Timeline):
    def construct(self):
        A = Text("Text-A")
        A.points.to_border(LEFT)

        A.show()
        self.forward()
        self.play(
            A.anim.points.scale(3).shift(RIGHT * 7 + UP * 2)
        )
        self.play(
            A.anim.color.set(BLUE)
        )
        self.forward()
class janim.anims.transform.FadeTransform(src: Item, target: Item, *, hide_src: bool = True, show_target: bool = True, path_arc: float = 0, path_arc_axis: Vect = array([0., 0., 1.]), path_func: PathFunc | None = None, src_root_only: bool = False, target_root_only: bool = False, collapse: bool = True, **kwargs)

基类:AnimGroup

class janim.anims.transform.TransformMatchingShapes(src: ~janim.items.item.Item, target: ~janim.items.item.Item, *, match: MatchHandler = <function TransformMatchingShapes.<lambda>>, mismatch: tuple[MismatchHandler, MismatchHandler] = (<function TransformMatchingShapes.<lambda>>, <function TransformMatchingShapes.<lambda>>), duration: float = 2, lag_ratio: float = 0, collapse: bool = True, **kwargs)

基类:AnimGroup

匹配形状进行变换

  • match 表示对于匹配的形状的处理

  • mismatch 表示对于不匹配的形状的处理

  • 注:所有传入该动画类的额外参数(**kwargs)都会被传入 matchmismatch 的方法中

TransformMatchingShapesExample
from janim.imports import *

class TransformMatchingShapesExample(Timeline):
    def construct(self):
        a = Text("the morse code", font_size=48).show()
        b = Text("here come dots", font_size=48)

        self.forward()
        self.play(TransformMatchingShapes(a, b, path_arc=PI/2))
        self.forward()
        self.play(TransformMatchingShapes(b, a, path_arc=PI/2))
        self.forward()
class janim.anims.transform.TransformMatchingDiff(src: ~janim.items.item.Item, target: ~janim.items.item.Item, *, match: MatchHandler = <function TransformMatchingDiff.<lambda>>, mismatch: tuple[MismatchHandler, MismatchHandler] = (<function TransformMatchingDiff.<lambda>>, <function TransformMatchingDiff.<lambda>>), duration: float = 2, lag_ratio: float = 0, collapse: bool = True, **kwargs)

基类:AnimGroup

匹配 diff 进行变换

对于一般物件,使用形状匹配 diff;对于 Text 使用 TextChar 的字符匹配 diff

  • match 表示对于匹配的形状的处理

  • mismatch 表示对于不匹配的形状的处理

  • 注:所有传入该动画类的额外参数(**kwargs)都会被传入 matchmismatch 的方法中

TransformMatchingDiffExample
from janim.imports import *

typ1_src = R"""
```python
subtitle_group = Group(
    SurroundingRect(subtitle,
                    color=surrounding_color,
                    stroke_alpha=0,
                    fill_alpha=surrounding_alpha),
    subtitle
).fix_in_frame()
subtitle_group.depth.set(depth)

if not self.hide_subtitles:
    self.schedule(range.at, subtitle_group.show)
    self.schedule(range.end, subtitle_group.hide)
```
"""

typ2_src = R"""
```python
if surrounding_alpha == 0:
    subtitle_display = subtitle
else:
    subtitle_display = Group(
        SurroundingRect(subtitle,
                        color=surrounding_color,
                        stroke_alpha=0,
                        fill_alpha=surrounding_alpha),
        subtitle
    )
subtitle_display.fix_in_frame().depth.set(depth)

if not self.hide_subtitles:
    self.schedule(range.at, subtitle_display.show)
    self.schedule(range.end, subtitle_display.hide)
```
"""


class TransformMatchingDiffExample(Timeline):
    CONFIG = Config(
        typst_shared_preamble=t_(
            R'''
            #set text(black)
            #show: box.with(inset: 8pt, fill: white, stroke: gray + 2pt, radius: 4pt)
            '''
        )
    )

    def construct(self):
        typ1 = TypstDoc(typ1_src).show()
        typ2 = TypstDoc(typ2_src)

        # 设置背景框的深度
        for typ in [typ1, typ2]:
            typ[:2].set(depth=1)

        self.forward()
        self.play(
            Transform(typ1[:2], typ2[:2], duration=2),
            TransformMatchingDiff(typ1[2:], typ2[2:])
        )
        self.forward()

        typ1 = TypstDoc(typ1_src).show()
        typ2 = TypstDoc(typ2_src)