Skip to main content

Animations

VizCraft provides two animation systems for different needs.

Two animation systems

SystemBest forHow it works
Timeline (data-only)Sequenced, scrub-able, exportable animationsCompiles to an AnimationSpec → plays via adapter
Registry / CSSContinuous decorative effectsCSS @keyframes applied to elements

Most users should start with the timeline system.

Timeline animations

Create an animation

Use builder.animate(callback) to define a timeline. Inside, target nodes and edges, then describe how properties change:

builder.animate((anim) => {
anim
.node('a')
.to({ x: 200, opacity: 1 }, { duration: 500, from: { x: 0, opacity: 0 } });
anim
.node('b')
.to({ y: 200 }, { duration: 300, delay: 200, easing: 'easeOut' });
});

Animatable properties

TargetProperties
Nodex, y, opacity, scale, rotation
Edgeopacity, strokeDashoffset
OverlayAny numeric custom parameter

Live example: slide out & back

Sequencing with .wait() and .at()

Use anim.wait(ms) to insert pauses, or anim.at(ms) to jump to an absolute time:

builder.animate((anim) => {
anim.node('a').to({ opacity: 1 }, { duration: 300, from: { opacity: 0 } });
anim.wait(200); // pause 200ms
anim.node('b').to({ opacity: 1 }, { duration: 300, from: { opacity: 0 } });
anim.at(1000); // jump to 1000ms
anim.node('c').to({ opacity: 1 }, { duration: 300, from: { opacity: 0 } });
});

Easing

Available easing functions: 'linear', 'easeIn', 'easeOut', 'easeInOut'.

anim.node('a').to({ x: 300 }, { duration: 500, easing: 'easeInOut' });

Per-element animations

You can also define animations directly on nodes and edges:

// Callback form
builder
.node('a')
.at(100, 100)
.circle(20)
.animate((anim) => {
anim.to({ scale: 1.5, opacity: 0.5 }, { duration: 400, easing: 'easeOut' });
});

// Quick transition form
builder
.node('b')
.at(200, 100)
.rect(60, 40)
.animateTo({ opacity: 0 }, { duration: 300, delay: 500 });

Extending the adapter

Add custom animatable properties by extending the animation adapter:

builder.animate((anim) => {
anim.extendAdapter((register) => {
register('r', {
get: (el) => (el.shape.kind === 'circle' ? el.shape.r : 0),
set: (el, val) => {
if (el.shape.kind === 'circle') el.shape.r = val;
},
});
});
anim.node('pulse').to({ r: 40 }, { duration: 500, from: { r: 20 } });
});

Playback

Playing animations

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

// Or play manually
const controller = builder.play();

// Playback controls
controller.pause();
controller.seek(500); // jump to 500ms
controller.play(); // resume
controller.stop(); // stop and reset

Multiple animation specs

const spec1 = builder.animate((anim) => {
/* ... */
});
const spec2 = builder.animate((anim) => {
/* ... */
});

// Play specific specs
builder.play(container, [spec1, spec2]);

CSS / registry animations

For continuous decorative effects, use the CSS animation system. Built-in: 'flow' (marching dashes on edges):

Registering custom CSS animations

import { CoreAnimationRegistry } from 'vizcraft';

CoreAnimationRegistry.registerEdge('pulse', (edge, opts) => ({
keyframes: `@keyframes pulse-${edge.id} { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }`,
style: `animation: pulse-${edge.id} ${opts?.duration ?? '2s'} infinite;`,
}));

// Use it
builder.edge('a', 'b').animate('pulse', { duration: '1.5s' });

Exporting animated frames

Export the SVG at the current animation frame (useful for server-side rendering or thumbnails):

const svgString = builder.svg({ includeRuntime: true });

For more details on the animation builder API, see the Animation API Reference.


Found a problem? Open an issue on GitHub.