transform

Note

Using Transform for transformations between different texts may not produce sufficiently good results; please consider carefully when using it

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)

Bases: Animation

Create an interpolation animation from src_item to target_item

Mechanism: hide the source item at the beginning, play the interpolation effect during the animation, and show the target item at the end

Parameters:
  • src_item – Starting item of the transform

  • target_item – Target item of the transform

Path and Interpolation

Parameters:
  • path_arc – Arc angle of the interpolation path; when 0, interpolation is along a straight line

  • path_arc_axis – Rotation axis when using an arc path.

  • path_func – Custom path function; when provided, it overrides the default path behavior of path_arc and path_arc_axis

Alignment Strategy

Parameters:
  • flatten – Whether to ignore child-item hierarchy and align by flattening in order; default is False, requiring source and target substructures to be alignable

  • root_only – Whether to interpolate only the root item without recursing into child items

Visibility and Fade

Parameters:
  • hide_src – Whether to automatically hide the source item when the animation starts

  • show_target – Whether to automatically show the target item when the animation ends

  • src_fade – Only effective when hide_src=False; indicates the duration ratio for source-item fade-in at the start of the animation

  • target_fade – Indicates the duration ratio for target-item fade-out at the end of the animation

src_fade and target_fade are useful for semi-transparent items, and can avoid abrupt opacity changes caused by overlap at the beginning/end


Basic example:

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()

Demonstration of hide_src and show_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()

Demonstration of src_fade and target_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)

Bases: Transform

See generate_target() for details

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)

Bases: AnimGroup

Transform between src and target according to the slice list


Basic Usage

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

Equivalent to

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

Omitting Target Slices

Use ... to indicate the same slice as the transformation source

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

Equivalent to

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

Consecutive Slices

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

Equivalent to

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

Slice Shorthand

If there is only one slice in total, one level of nesting can be omitted

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

Equivalent to

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

Consecutive Slices in Reverse

Writing it backwards will reverse the slices

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

Equivalent to

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

Note the left-closed right-open principle in Python slicing; for reverse sequences like [8, 6, 4, 0], it is left-open right-closed

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)

Bases: Transform

Tweening process created based on the transformation of the item.

See anim() for details.

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)

Bases: 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)

Bases: AnimGroup

Transform by matching shapes

  • match indicates how to handle shapes that do match

  • mismatch indicates how to handle shapes that do not match

  • Note: All additional parameters (**kwargs) passed to this animation class will be passed to the methods in match and mismatch

TransformMatchingShapesExample
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)

Bases: AnimGroup

Transforms based on matching differences.

For general items, uses shape matching diff; for Text, uses character matching diff based on TextChar.

  • match indicates how to handle shapes that do match

  • mismatch indicates how to handle shapes that do not match

  • Note: All additional parameters (**kwargs) passed to this animation class will be passed to the methods in match and mismatch

TransformMatchingDiffExample
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)

        # Set the depth of the background box
        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)