How-to
Build an explainer with chapters
An explainer is a timeline with named steps. The reader presses next, the figure plays to the end of the step, and stops.
import { Scene, Timeline, box, ground, label, line, fadeIn, draw, moveBy, stagger } from 'cseen';
const s = new Scene({ fit: 'sphere', title: 'How a request travels' });s.add(ground());const client = s.add(box({ at: [-3, 0, 0], size: 1 }));const server = s.add(box({ at: [3, 0, 0], size: 1, role: 'accent' }));const wire = s.add(line(client.pt('center'), server.pt('center')));const note = s.add(label('request', [0, 0, 1.4], { anchor: 'middle' }));
const tl = new Timeline();tl.chapter('machines').play(stagger(0.3, fadeIn(client), fadeIn(server)));tl.chapter('connect').play(draw(wire, 0.8));tl.chapter('label').play(fadeIn(note));tl.chapter('move').play(moveBy(server, [0, 2, 0], 1));tl.lockFit(s);chapter('name') marks the time where the next animation starts. Each chapter is a step.
Driving it from buttons
Section titled “Driving it from buttons”const player = mount(el, s, { timeline: tl, autoplay: false });
nextButton.onclick = () => player.step(1);backButton.onclick = () => player.step(-1);step() jumps to the start of the next (or previous) chapter. To play up to the end of a step instead of jumping, seek there with player.seek(t) and play from it; tl.chapters lists each name and time.
Keep it readable
Section titled “Keep it readable”- Use
lockFit(orfit: 'sphere') so the picture does not change size between steps. - One idea per chapter. Fade things in in the order the reader needs them.
- Give the scene a
titleand adesc, and put the explanation in the text beside it, not only in the figure.
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));