Explanation
Timeline and animation
Animation in cseen is a function of time. A timeline is a list of tweens, and seek(t) rewinds everything to its starting state and replays up to t. So the picture depends only on the time: playing, pausing, scrubbing backwards and rendering one frame all work the same way.
Tweens
Section titled “Tweens”Any property of any node, or of the camera, can be animated:
import { Scene, Timeline, box, tween } from 'cseen';
const s = new Scene();const cube = s.add(box({ at: [0, 0, 0] }));
const tl = new Timeline();tl.play(tween(cube, { opacity: 0.2, pos: [4, 0, 0] }, { duration: 1.2, ease: 'out' }));Properties are plain values: pos, rot, scale, opacity, draw, alpha, a camera’s yaw, pitch and zoom, and each object’s own: a chart’s grow and data, a progress bar’s value, a wave’s phase, a stereo rig’s baseline.
Easing names are linear, in, out, inOut (the default), smooth and back, or pass your own function.
Ready-made animations
Section titled “Ready-made animations”fadeIn(n) fadeOut(n) draw(n) moveTo(n, p) moveBy(n, d) rotateTo(n, r) scaleTo(n, k) typeOn(text)Playing backwards
Section titled “Playing backwards”reverse(anim) returns the same animation played backwards, so a figure can be built and then taken apart. Give every tween a from and a literal to. See Constructions.
Combining
Section titled “Combining”seq(a, b, c) // one after anotherpar(a, b) // togetherstagger(0.2, a, b) // together, each 0.2 s after the lastwait(1) // a gaptimeline.play(x) appends x after what is already there. timeline.at(time, x) places it at a time.
Drag to turn, scroll or pinch to zoom, double click to reset.
import { Scene, Timeline, arrow, draw, fadeIn, ground, seq, stagger, wait } from 'cseen';
import { micStand, monitor, speaker } from 'cseen/devices';
const s = new Scene();
s.add(ground());
const mic = s.add(micStand({ at: [-3, 0, 0] }));
const mon = s.add(monitor({ at: [0, 1, 0], width: 3 }));
const spk = s.add(speaker({ at: [3.2, 0, 0] }));
const a = s.add(arrow(mic.anchor([0, 0, 1.6]), mon.anchor([-1.4, -0.1, 1.1]), { role: 'accent' }));
const b = s.add(arrow(mon.anchor([1.4, -0.1, 1.1]), spk.anchor([0, -0.3, 1.2]), { role: 'accent' }));
const tl = new Timeline();
tl.play(stagger(0.35, fadeIn(mic), fadeIn(mon), fadeIn(spk)));
tl.chapter('connect').play(seq(draw(a, 0.8), draw(b, 0.8)));
tl.play(wait(0.8));
Chapters
Section titled “Chapters”tl.chapter('name') marks a step. player.step() jumps to the next one, which suits explainers. See Build an explainer.
Keeping the picture still
Section titled “Keeping the picture still”By default the scene refits to its contents every frame, so a moving thing makes the picture breathe. tl.lockFit(scene) fixes the framing to fit every moment of the timeline. Call it once after building the timeline.
Playing it
Section titled “Playing it”mount plays a timeline in the page and returns a player:
const player = mount(el, s, { timeline: tl, loop: { pause: 1.2 } });player.pause();player.seek(1.5);player.step(); // next chapterIt animates only while on screen, caps the frame rate with maxFps, and shows the final frame for visitors who prefer reduced motion. player.invalidate() redraws after a change you made by hand, such as moving the camera.
Animations that are not in the timeline
Section titled “Animations that are not in the timeline”Dashes that travel along a line (flow: true) run in CSS rather than in the timeline, so they move even in a plain, static SVG. Waves are a function of phase, which is periodic, so a looping timeline is seamless.
Drag to turn, scroll or pinch to zoom, double click to reset.
import { Scene, Timeline, arrow, draw, ground, link, par, pulse, tween, wait } from 'cseen';
import { cloud, laptop, server } from 'cseen/devices';
const s = new Scene();
s.add(ground());
s.add(laptop({ at: [-4.2, 0, 0], width: 2.2 }));
const net = s.add(cloud({ at: [0, 2.4, 2], size: 2.2 }));
s.add(server({ at: [4.2, 0, 0], width: 1.4 }));
// dashes that travel: runs in CSS, so it moves even in a static SVG
s.add(arrow([-4.2, -1.6, 0.02], [4.2, -1.6, 0.02], { role: 'faint', flow: true }));
// curved arrows between objects, which follow them if they move
const up = s.add(link([-3.2, 0, 1.4], net, { bend: 0.2, flow: 1.5, role: 'accent' }));
const down = s.add(link(net, [4, 0, 1.2], { bend: 0.2, role: 'ink', dash: '2 3', flow: 1 }));
// a pulse rides a link; you decide when and how fast
const ping = s.add(pulse(up, { trail: 4, r: 3.4 }));
const pong = s.add(pulse(down, { trail: 4, r: 3.4, role: 'ink' }));
const tl = new Timeline();
tl.play(par(draw(up, 0.8), draw(down, 0.8)));
tl.play(tween(ping, { along: 1 }, { duration: 1.6, from: { along: 0 }, ease: 'linear' }));
tl.play(tween(pong, { along: 1 }, { duration: 1.6, from: { along: 0 }, ease: 'linear' }));
tl.play(wait(0.6));