Files
hyperframes/skills/hyperframes-animation/rules/reactive-displacement.md
WaterrrForever 853256403b feat(skills): c2v mining pass — 7 new blueprints, 10 new rules, compacted recipe corpus (#2680)
* feat(skills): c2v mining pass over animation blueprints and rules

Compacts ~45 existing animation rules/blueprints into tighter recipe form
(net -3.4k lines) and adds 17 mined from the c2v corpus:

- 7 blueprints: agent-progress-theater, camera-journey, fixed-anchor-cycle,
  panel-edit-live-sync, prompt-type-submit-generate,
  transcript-scroll-artifact-reveal, zoom-out-workspace-reveal
- 10 rules: 3d-camera-flight, anchored-layout-expand, chart-scrub-readout,
  chromatic-glitch, control-target-sync, cursor-drag, gradient-text-sweep,
  multi-cursor-choreography, particle-burst, theme-crossfade-morph






Both indexes updated.

* feat(skills): sync product-launch script bank with mined blueprint roles

The role->blueprint script bank in product-launch-video/story-design.md is
kept 1:1 with blueprints-index role declarations, which the c2v mining pass
expanded. Adds the 25 missing entries (script-shape descriptor + example
lines + pattern): 13 for the 7 new blueprints, 12 for role widenings on 7
existing ones (cursor-ui-demo, dataviz-countup, titlecard-reveal, et al.),
and states the 1:1 sync contract in the bank's intro.

* docs(skills): cover constellation-hub scatter-drift variant in the script bank

Review follow-up on #2680: the SOCIAL_PROOF constellation-hub entry
patterned only the orbit shape; the c2v pass added a scatter-drift
end-card variant with the opposite geometry (no hub, no ring). Adds an
example line and extends the pattern so a scatter-drift beat's VO isn't
steered toward the orbit shape.
2026-07-21 17:28:55 +08:00

6.4 KiB
Raw Permalink Blame History

name, description, metadata
name description metadata
reactive-displacement Physical collision where an entering element's spring drives the exiting element's displacement — single source of truth makes the motion causally linked.
tags
transition, physics, collision, displacement, spring, causal

Reactive Displacement

Exit animation of element A is mathematically DERIVED from the entry spring of element B — a causal link: "A moves because B hit it." Distinct from scale-swap-transition.md (which overlaps but isn't causal) and card-morph-anchor.md (one container morphing).

A single 0→1 driver tween (the "entry spring") feeds three concurrent derived motions in one onUpdate:

  • Intruder (B, entering): position interpolated off-stage → settled over the full driver, plus tilt settling to 0° and a sharp early opacity reveal.
  • Victim (A, exiting): position interpolated settled → off-stage in the OPPOSITE direction, completing at VICTIM_FRACTION (~0.40.5) of the driver — NOT 1.0.

The victim finishing BEFORE the intruder's entry creates the "hit then settle" rhythm; sharing one eased driver makes the impact moment mathematically synchronized.

Recipe

// Both cards absolutely centered; overflow: hidden on the scene (off-stage travel);
// will-change: transform, opacity on both; intruder z-index ABOVE victim.
const INTRUDER_START_X = STAGE_W; // off-stage right
const VICTIM_END_X = -STAGE_W; // off-stage left — SAME axis, opposite direction

gsap.set("#victim", { x: 0, opacity: 1, rotation: 0 });
gsap.set("#intruder", { x: INTRUDER_START_X, opacity: 0, rotation: -INTRUDER_TILT });

const driver = { p: 0 };
tl.to(
  driver,
  {
    p: 1,
    duration: DRIVER_DUR,
    ease: `back.out(${BOUNCE_FACTOR})`, // the intruder spring
    onUpdate: () => {
      // Intruder: full 0→1 progress maps enter (off-stage → center)
      const intruderX = INTRUDER_START_X * (1 - driver.p);
      const intruderOpacity = Math.min(1, driver.p * FADE_IN_SHARPNESS);
      const intruderRot = -INTRUDER_TILT * (1 - driver.p); // settles to 0°
      const intruder = document.getElementById("intruder");
      intruder.style.transform = `translate(-50%, -50%) translateX(${intruderX}px) rotate(${intruderRot}deg)`;
      intruder.style.opacity = String(intruderOpacity);

      // Victim: completes its exit at VICTIM_FRACTION of the driver — by the
      // time the intruder centers, the victim is already off-stage.
      const victimP = Math.min(1, driver.p / VICTIM_FRACTION);
      const victimX = VICTIM_END_X * victimP;
      const victim = document.getElementById("victim");
      victim.style.transform = `translate(-50%, -50%) translateX(${victimX}px)`;
      victim.style.opacity = String(1 - victimP);
    },
  },
  DRIVER_AT,
);
// Climax dwell — intruder holds centered for ≥ DWELL_MIN before the scene ends.

Variations

  • Impact rotation on victim — the victim also rotates as it slides: const victimRot = victimP * -VICTIM_KICK_DEG; appended to its transform. VICTIM_KICK_DEG 1525°, magnitude matched to the perceived intruder weight.
  • Vertical collision — intruder from top, victim displaced downward; same math on Y. Reads as "weight dropped on it."
  • Wobble after settle — after the intruder centers, a damped sine wobble (±WOBBLE_AMP_DEG rotation, linearly decaying over WOBBLE_DUR via a second ease: "none" driver at DRIVER_AT + DRIVER_DUR) before stillness — "impact aftermath."
  • Multi-victim ripple — the intruder displaces multiple aligned cards, each victim's victimP on a slightly offset driver phase (cascade ripple).

Values

token range notes
DRIVER_AT phase-dependent after the prior reading beat resolves; must leave ≥ DWELL_MIN of climax dwell before the scene ends
DRIVER_DUR 0.61.4 s short = zippy punch, long = heavy landed impact; higher bounce on long durations reads as floaty
BOUNCE_FACTOR 1.22.0 (typ. 1.41.6) stay in the back.out family (or elastic.out for oscillation) — changing family rewrites the feel
VICTIM_FRACTION 0.40.5 <0.4 the victim disappears before the impact reads; >0.5 feels parallel, not causal; hard cap ~0.6
STAGE_W ≥ composition width smaller leaves the off-stage element partially visible at start
INTRUDER_TILT 515° (typ. ~10°) low = clean glide, high = "spin-and-plant"; sign consistent with entry direction (momentum transfer)
FADE_IN_SHARPNESS 38 intruder reaches opacity 1 at 1/FADE_IN_SHARPNESS of progress; must be > 1 or it's transparent at center
DWELL_MIN ≥ 1.0 s (typ. 1.01.5) post-impact dwell is where the new content gets read — do not skip

Critical Constraints

  • Single driver = single source of truth — both motions computed inside ONE driver's onUpdate, never separate tl.to() calls per element; independent tweens destroy the causal link (they'd merely be near each other in time).
  • Victim completes at a fraction of the driver — the "hit" is the overlap moment; after it the victim is just vacating space the intruder will fill.
  • Directional momentum transfer — same axis, opposite directions; different axes read as passing, not colliding.
  • Intruder z-index above victim — explicit, not DOM order; otherwise the victim looks like it tunneled through.
  • Intruder enters tilted, settles flat — small initial tilt → 0° reads as "spinning in then planting."
  • Climax dwell after impact — the impact is the headline beat; hold the settled intruder ≥ DWELL_MIN.
  • overflow: hidden on the scene — off-stage motion exceeds the frame.

See also

control-target-sync (the live-editing mirror — repeated coupled edits, nothing exits) · hacker-flip-3d (intruder text reveal during entry) · sine-wave-loop (idle breathing during the dwell) · vertical-spring-ticker (a ticker that "shoves" the previous content out).