4.2 KiB
motion — animation runtime
EngineMotion is the runtime engine for the UIX motion system: it registers
motion presets and runs them. It is an art (a pure runtime artifact,
public methods over private state, no reactive $state) so that both UIX
layers can consume it via uix.motion without a cross-layer dependency:
- soma (
Presence) callsmotion.run(node, phase)to run + await a JS preset (aspring) before unmounting — the gating soma can't get fromgetAnimations()alone. - eidos generates the CSS (keyframes + preset rules), registers its visual presets into the engine, and drives wrappers.
The engine owns the execution; eidos owns the CSS generation and the
visual presets data. Neither imports the other — they meet at uix.motion.
The two moments
Motion occurs in two moments, each animable (one, the other, or both):
--event |
--state |
|
|---|---|---|
| Attr | data-event-* (sema) |
data-state (soma) |
| What | the perceptual firma during a signal's hold | the transition to/from a persistent condition |
| Registry | signatures (eidos generates CSS) |
presets (named, per-component) |
EngineMotion runs the --state presets (CSS → settled; JS → driver). The
--event firma is CSS that eidos generates from signatures; the engine does
not run it.
API
const motion = createEngineMotion({ dom }) // dom: a MotionDom port
motion.register('scale-fade', { driver: 'css', … }) // eidos registers presets
motion.register('pop', { driver: 'spring', enter: spring({ … }) })
motion.run(node, 'enter') // resolve node's data-animation-style + run
motion.enter(el, 'pop') // run a preset by name (enter phase)
motion.exit(el, 'pop') // run a preset by name (exit phase)
motion.cancel(el) // cancel active JS motion on el
await motion.pending(el) // combined finished, for Presence
motion.dispose() // idempotent: cancel all + clear registry
// registry introspection
motion.has('pop') // is a preset registered?
motion.resolve('pop') // the StatePreset, or undefined
motion.list() // registered preset names
- CSS preset → declarative: a settled handle (the generated CSS + soma's
PresenceviagetAnimations()do the work). - JS preset → runs its
MotionRundriver, normalises the result (Animation|Animation[]|MotionHandle) to one handle, tracks it per element.pending(el)is what Presence awaits for drivers (spring) thatgetAnimations()can't see.
Drivers (./drivers)
MotionRun factories for JS presets:
spring— self-contained physics: an independent semi-implicit-Euler spring per property, stepped viactx.dom.requestFrame. Overshoot + settle — the curve no cubic-bezier expresses. Cancellable viactx.signal.waapi—el.animate(...). TheAnimationis ingetAnimations(), so Presence awaits it for free.rect— FLIP: measures first/last rects, animates the inverse delta via WAAPI (layout / shared-element transitions).
JsDriver also declares 'svelte', but it is reserved / not yet
implemented — no driver dispatches it. The three drivers above are the whole
set today.
The DOM port
This art imports no other art. The DOM dependency arrives injected and is
typed by the structural MotionDom port (requestFrame / cancelFrame /
prefersReducedMotion); the real ActiveDom satisfies it.
Composition
// active-app (attach path)
const App = createActiveApp({
services: { dom: defineActiveDom(), motion: defineEngineMotion() }
});
// active-uix (standalone) creates it directly and exposes uix.motion.
Without a dom service the engine degrades (JS drivers settle; CSS is
declarative regardless). duration / ease are string (token keys or raw
values); eidos validates the keys — this art stays token-agnostic.