Animating SmartArts

All item-based SmartArt components (ChevronProcess, Cycle, GridLayout, Pyramid, BoxList, BulletPoints, TreeNode, MindMapNode) follow the unified Pre-Build & Mutate lifecycle:

smartarts_1
SmartArt Animation Overview: Progressive Reveal and Active Highlighting Without Layout Jumping
Source Code
from drawlib.anim import Animation
from drawlib.canvas import save, setup
from drawlib.smartarts import BoxList, ChevronProcess
from drawlib.styles import Styles

setup(width=120, height=40)
anim = Animation(fps=2.0)

proc = ChevronProcess(
    style=Styles.Neutral,
    text_style=Styles.DarkBold.patch(text_size=11.0),
    description_style=Styles.Dark,
    flat_left_end=True,
)
chev_items = [
    proc.add("1. Plan"),
    proc.add("2. Build"),
    proc.add("3. Verify"),
    proc.add("4. Deploy"),
]

cards = BoxList(
    style=Styles.Neutral,
    text_style=Styles.Dark.patch(text_size=10.5),
)
box_items = [
    cards.add("Spec Locked"),
    cards.add("Artifact Built"),
    cards.add("Checks Green"),
    cards.add("Prod Live"),
]

for step in range(len(chev_items)):
    for i, (c_item, b_item) in enumerate(zip(chev_items, box_items)):
        is_visible = (i <= step)
        is_active = (i == step)
        c_item.show = is_visible
        b_item.show = is_visible
        c_item.style = Styles.PrimaryFlat if is_active else Styles.PrimaryNeutral
        c_item.text_style = Styles.WhiteBold.patch(text_size=11.0) if is_active else Styles.DarkBold.patch(text_size=11.0)
        b_item.style = Styles.SecondaryNeutral if is_active else Styles.Neutral
        b_item.text_style = Styles.DarkBold.patch(text_size=10.5) if is_active else Styles.Dark.patch(text_size=10.5)

    is_last = (step == len(chev_items) - 1)
    with anim.frame(duration=2.5 if is_last else 0.7):
        proc.draw(xy=(4.0, 21.0), width=112.0, height=14.0)
        cards.draw(xy=(4.0, 5.0), box_width=28.0, box_height=11.5, align="bottom_left")

save()
  1. Instantiate the component and register all items once via item = comp.add(..., show=...) outside the loop.
  2. Inside with anim.frame():, mutate item.show, item.style, or item.text_style, and call comp.draw(xy=..., scale=1.0).

1. Mutable Item Classes, item.show = False & Proportional Scaling (scale=1.0)

Every item-based SmartArt returns a mutable item model from .add(..., show=True) (and cycle.center exposes CycleCenter), allowing you to mutate visibility, styles, and text across frames before calling draw(..., scale=1.0) or draw_flexible(..., scale=1.0):

SmartArt Component Returned Item Class Mutable Attributes Rendering Method & scale Support
ChevronProcess ChevronItem .show, .style, .text_style, .description_style, .text, .description draw(xy, width, height, scale=1.0)
Cycle CycleItem, CycleCenter (cycle.center) .show, .style, .text_style, .description_style, .arrow_style, .text, .description draw(xy, radius, align="center", scale=1.0)
GridLayout GridItem .show, .style, .text_style, .text, .position, .width, .height draw(xy, width, height, margin, scale=1.0), draw_flexible(..., scale=1.0)
Pyramid PyramidItem .show, .style, .text_style, .text draw(xy, width, height, margin, scale=1.0), draw_flexible(..., scale=1.0)
BoxList BoxListItem .show, .style, .text_style, .text draw(xy, box_width, box_height, margin, direction, scale=1.0)
BulletPoints BulletPointItem .show, .style (alias for .text_style), .text_style, .text, .indent draw(xy, scale=1.0)
TreeNode TreeNode .show, .text_style, .line_style, .text draw(xy, scale=1.0)
MindMapNode MindMapNode .show, .style, .text_style, .line_style, .text draw(xy, branch=..., scale=1.0)
Table (Stateful Table) reset_styles(), set_style_cell(..., rows=..., columns=...) draw(xy, width, height, data, scale=1.0), draw_flexible(..., scale=1.0)

When item.show = False on any SmartArt item:

  • The hidden item still reserves its exact spatial slot (chevron width, orbit angle, grid cell, pyramid tier, or tree row).
  • Revealing items step by step via item.show = (i <= step) never causes already-visible items to jump or resize.
  • Passing scale != 1.0 to draw(..., scale=s) or draw_flexible(..., scale=s) uniformly scales all shapes, margins, line widths, and font sizes relative to the anchor coordinate xy.

2. Linear Pipelines & Cycles (ChevronProcess, Cycle)

Build a ChevronProcess or Cycle once outside the loop, keep the list of returned ChevronItem or CycleItem objects (plus cycle.center (CycleCenter) for radial cycles), and mutate .show, .style, and .text_style across frames. In Cycle, hidden steps (item.show = False) preserve their angular orbit positions and automatically hide their outgoing connecting arrows:

from drawlib.anim import Animation
from drawlib.canvas import save, setup
from drawlib.smartarts import ChevronProcess
from drawlib.styles import Styles

setup(width=114, height=30)
anim = Animation(fps=1.5)

proc = ChevronProcess(
    style=Styles.Neutral,
    text_style=Styles.DarkBold.patch(text_size=11.0),
    description_style=Styles.Dark,
    flat_left_end=True,
)
items = [
    proc.add("1. Plan"),
    proc.add("2. Build"),
    proc.add("3. Verify"),
    proc.add("4. Deploy"),
]

for step in range(len(items)):
    for i, item in enumerate(items):
        item.show = (i <= step)
        item.style = Styles.PrimaryFlat if i == step else Styles.PrimaryNeutral
        item.text_style = Styles.WhiteBold.patch(text_size=11.0) if i == step else Styles.DarkBold.patch(text_size=11.0)

    is_last = (step == len(items) - 1)
    with anim.frame(duration=2.5 if is_last else 0.8):
        proc.draw(xy=(4.0, 6.5), width=106, height=17)

save()
smartarts_2
Step-by-Step Pipeline Reveal with Active Stage Highlight

Similarly, for Cycle:

from drawlib.anim import Animation
from drawlib.canvas import save, setup
from drawlib.smartarts import Cycle
from drawlib.styles import Styles

setup(width=76, height=68)
anim = Animation(fps=1.5)

cycle = Cycle(
    style=Styles.Neutral,
    text_style=Styles.DarkBold.patch(text_size=10.5),
    arrow_style=Styles.DarkBold,
    node_shape="circle",
    node_radius=7.5,
)
cycle.set_center(
    text="PDCA",
    radius=9.0,
    style=Styles.SecondaryNeutral,
    text_style=Styles.DarkBold.patch(text_size=11.0),
)
steps = [
    cycle.add("1. Plan"),
    cycle.add("2. Do"),
    cycle.add("3. Check"),
    cycle.add("4. Act"),
]

for step_idx in range(len(steps)):
    for i, item in enumerate(steps):
        item.show = (i <= step_idx)
        item.style = Styles.PrimaryFlat if i == step_idx else Styles.PrimaryNeutral
        item.text_style = (
            Styles.WhiteBold.patch(text_size=10.5) if i == step_idx else Styles.DarkBold.patch(text_size=10.5)
        )

    is_last = (step_idx == len(steps) - 1)
    with anim.frame(duration=2.5 if is_last else 0.8):
        cycle.draw(xy=(38.0, 34.0), radius=21.0)

save()
smartarts_3
Progressive Step Reveal and Active Highlighting on a Cycle SmartArt

3. Matrix Grids & Tables (GridLayout, Table)

In GridLayout, grid.add((col, row), width, height, ...) returns a mutable GridItem (cell.show, cell.style, cell.text_style). In Table, call table.reset_styles() and table.set_style_cell(..., rows=[active_row]) before table.draw(xy, width, height, data) to sweep row highlights down a matrix:

from drawlib.anim import Animation
from drawlib.canvas import save, setup
from drawlib.smartarts import Table
from drawlib.styles import Colors, Styles

setup(width=114, height=46)
anim = Animation(fps=1.5)

tbl = Table(
    cell_style=Styles.White,
    text_style=Styles.Dark.patch(text_size=10.5),
    header_cell_style=Styles.PrimaryFlat,
    header_text_style=Styles.WhiteBold.patch(text_size=11.0),
    border_style=Styles.DarkThin,
)

data = [
    ["Service", "SLA Target", "P95 Latency"],
    ["API Gateway", "99.99%", "12 ms"],
    ["Auth Service", "99.95%", "18 ms"],
    ["Order Engine", "99.90%", "24 ms"],
]

for row_idx in [1, 2, 3]:
    tbl.reset_styles()
    tbl.set_style_cell(
        background_color=Colors.Primary1,
        text_style=Styles.PrimaryBold.patch(text_size=10.5),
        rows=[row_idx],
    )
    is_last = (row_idx == 3)
    with anim.frame(duration=2.5 if is_last else 0.8):
        tbl.draw(xy=(4.0, 41.0), width=106, height=36, data=data)

save()
smartarts_4
Table Row Highlight Sweep Across Frames

4. Hierarchical Trees & MindMaps (TreeNode, MindMapNode)

For TreeNode and MindMapNode, instantiate the hierarchy once outside the loop and mutate .show across frames before calling root.draw(xy=..., scale=1.0):

  • Setting child.show = False hides the child label, the incoming branch line from its parent, and its entire subtree, while preserving the exact vertical Y-coordinates of all subsequent sibling nodes:
from drawlib.anim import Animation
from drawlib.canvas import save, setup
from drawlib.icons import phosphor
from drawlib.smartarts import TreeNode
from drawlib.styles import Styles

setup(width=96, height=52)
anim = Animation(fps=1.5)

TreeNode.register_drawing_item(
    name="folder", location="before", padding_width=4.2, function=phosphor.folder,
    style=Styles.PrimaryFlat, args={"width": 3.4},
)
TreeNode.register_drawing_item(
    name="file", location="before", padding_width=4.2, function=phosphor.file_text,
    style=Styles.Dark, args={"width": 3.4},
)

n_api = TreeNode("api.py", text_style=Styles.Dark.patch(text_size=11.0), show=False).set_drawing_item("file")
n_auth = TreeNode("auth.py", text_style=Styles.Dark.patch(text_size=11.0), show=False).set_drawing_item("file")
n_services = TreeNode("services/", text_style=Styles.DarkBold.patch(text_size=11.0), children=[n_api, n_auth], show=False).set_drawing_item("folder")
n_main = TreeNode("main.py", text_style=Styles.Dark.patch(text_size=11.0), show=False).set_drawing_item("file")

root = TreeNode(
    "src/",
    text_style=Styles.DarkBold.patch(text_size=11.0),
    line_style=Styles.DarkThin,
    line_horizontal_margin=3.0,
    line_horizontal_length=3.5,
    line_vertical_margin=6.0,
    children=[n_services, n_main],
).set_drawing_item("folder")

steps = [
    (False, False, False, False),
    (True, False, False, False),
    (True, True, True, False),
    (True, True, True, True),
]

for idx, (s_svc, s_api, s_auth, s_main) in enumerate(steps):
    n_services.show = s_svc
    n_api.show = s_api
    n_auth.show = s_auth
    n_main.show = s_main
    is_last = (idx == len(steps) - 1)
    with anim.frame(duration=2.5 if is_last else 0.8):
        root.draw(xy=(20, 44))

save()
smartarts_5
Progressive TreeNode Hierarchy Reveal