vaweGitHub

MovesProduct moments

Panel live sync

look: vawe3.8 spanel-live-sync.md

Panel live sync: final frame of the product UI animation
Tap the clip to play it.

When to use

Use when the feature is editing with instant feedback: a visual editor, an inspector, a design tool. The page holds two surfaces in one frame, a target (a button on a page) and a panel of controls. A cursor presses a field and scrubs it, and the field's number and the target change in the same frame, three beats in a row (rotate, radius, padding). The claim is the causality: one gesture, two surfaces, no lag. Clip: panel-live-sync.mp4. Demo: demo/panel-live-sync.html.

The code

js
import '../../core/engine/page-api.js';
const smooth = easeFn('settle'), clamp = (u) => Math.min(1, Math.max(0, u)), lerp = (a, b, u) => a + (b - a) * u;
const seg = (t, a, b) => smooth(clamp((t - a) / (b - a)));
// one gesture per beat: glide to a field, press, scrub, release (seconds); values hold between beats
const beats = [
  { k: 'rot', glide: [0.35, 0.8], press: 0.86, scrub: [0.9, 1.45], release: 1.55 },
  { k: 'rad', glide: [1.55, 1.9], press: 1.95, scrub: [2.0, 2.55], release: 2.65 },
];
const value = { rot: (t) => lerp(0, -9, seg(t, ...beats[0].scrub)), rad: (t) => lerp(14, 58, seg(t, ...beats[1].scrub)) };

vawe.onFrame((t) => {
  const rot = value.rot(t), rad = value.rad(t);
  // the control and the target read the same numbers in the same frame, so they cannot drift apart
  target.style.rotate = `${rot}deg`;
  target.style.borderRadius = `${rad / 10.8}vh`;
  fields.rot.textContent = `${Math.round(rot)} deg`;
  fields.rad.textContent = `${Math.round(rad)} px`;
  for (const b of beats) fields[b.k].classList.toggle('on', t >= b.press && t < b.release);   // accent ring while pressed
  const [x, y] = cursorAt(t);   // glide on a smoothstep, then ride the scrub to the right
  cursor.style.translate = `${x}px ${y}px`;
  cursor.firstChild.style.scale = beats.some((b) => t >= b.press && t < b.release) ? 0.86 : 1;
});

Easing and timing

The clip runs 3.8 s. The look is vawe.

The clip uses 3 named eases from core/motion/presets.js. Each is a pair of After Effects handles: influence is how far along the segment the handle reaches, and speed is how fast the value moves at the key, as a multiple of the segment's average speed. Motion in the docs explains both.

EaseLeaving a keyArriving at a key
land12% influence, speed 4.875% influence, speed 0
pop12% influence, speed 4.835% influence, speed -1.5
settle60% influence, speed 060% influence, speed 0

Notes

Drive the cursor, the field text and the target from one vawe.onFrame, not from separate animations: two tweens that share a start time still drift when one is edited, and one function cannot. Keep both surfaces fully in frame for the whole clip; a zoom that crops the target out breaks the claim. Show the value with its unit (-9 deg, 58 px) in a mono face with tabular figures so the digits do not jitter. Scrub to the right by about 7 percent of the frame width so the drag is visible on the cursor. Measure the field positions with getBoundingClientRect before any animation starts, since an entrance translate would shift them. The target wears a dashed outline (selection chrome) so the viewer knows what is bound.

Looks (ground, type, radius, accent) live in demo/demo.css; this snippet keeps neutral tokens.

Sound

Sound: none; a scrub is silent, and a tick per value reads as a machine.