Skip to main content

Animations

VizCraft supports two complementary animation systems:

  1. Data-only timeline animations (AnimationSpec) (preferred) — authored with a fluent callback and played via builder.play().
  2. Registry/CSS animations (legacy / lightweight) — e.g. .animate('flow') adds CSS classes/vars.

Both approaches render efficiently by patching the mounted SVG in-place.

For a full authoring reference, see Animation Builder API.


1) Data-only animations (AnimationSpec)

Data-only animations are authored as a portable timeline (an AnimationSpec) containing numeric tweens (TweenSpec).

Mental model

  • You compile a timeline with builder.animate((aBuilder) => ...).
  • VizCraft stores compiled specs on the scene as scene.animationSpecs.
  • You play them with builder.play().

Under the hood, the player updates node.runtime / edge.runtime and VizCraft patches only the relevant SVG attrs/styles.


Create an AnimationSpec with builder.animate(cb)

This is the most direct way to author a timeline.

note

The live preview below uses a builder declared at the top of this MDX file.


Targeting: nodes vs edges

  • aBuilder.node('id') targets node:<id>
  • aBuilder.edge('a->b') targets edge:a->b (edge id form)
  • aBuilder.edge('a', 'b') is a convenience that compiles to edge:a->b

Element-level authoring: animate(cb) and animateTo(...)

If you prefer to attach data-only motion closer to where an element is defined:

  • node.animate(cb) / edge.animate(cb) compiles and stores an AnimationSpec.
  • node.animateTo(props, opts) / edge.animateTo(props, opts) is sugar for a single .to(...) step.
tip

edge.animate('flow') is still the registry/CSS system; edge.animate(cb) is the data-only timeline system.


Timeline control: .wait(ms) and .at(ms)

By default, .to(...) calls are sequential: each .to advances an internal “cursor”.

  • Use .wait(…) to insert a gap.
  • Use .at(…) to jump the cursor.
note

The live preview below uses a builder declared at the top of this MDX file.


Custom properties (advanced)

An AnimationSpec can target any string property (not just the core ones), as long as the playback adapter knows how to read/write it.

You can register custom properties directly on the aBuilder builder via aBuilder.extendAdapter(...) (ExtendAdapter). Here’s an example that animates a circle node’s radius via a custom prop named r.


Supported properties (core adapter)

Out of the box, the core player knows how to animate these numeric properties:

  • Node: x, y, opacity, scale, rotation
  • Edge: opacity, strokeDashoffset

AnimationSpec itself supports any string property, but playback only works for properties that the adapter registers.


Playback API

  • builder.mount(container) stores the container internally
  • builder.play() replays the stored scene.animationSpecs
  • If play() is called before mount(), it logs a warning and does nothing

Multiple specs

Every call to builder.animate(...) (or element-level animate/animateTo) appends another spec to scene.animationSpecs. builder.play() combines all of them and plays them together (tweens keep their own delays).

builder.play(container, spec)

If you want to play a one-off spec without storing it on the builder, you can pass a spec explicitly.

mount(container, { autoplay: true })

If you prefer a single call:

builder.mount(container, { autoplay: true });

How stopping works

controller.stop() restores runtime properties back to their captured “base” values.

Tip: If you’re mixing manual node.runtime = ... updates with timeline playback, decide which one is authoritative at any given time.


2) Registry/CSS animations (e.g. flow)

Registry animations are useful when you want a simple, reusable effect that doesn’t need per-frame JS interpolation.

  • You attach an animation by name (like flow).
  • VizCraft adds an animation CSS class and optional CSS variables.

Edge flow


Node pulse