mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-05 00:56:23 +00:00
* 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.
102 lines
6.5 KiB
Markdown
102 lines
6.5 KiB
Markdown
---
|
||
name: physics-press-reaction
|
||
description: Cursor + element synchronized press via subtractive spring forces — cursor lands on element, both compress together, then release. Distinct from press-release-spring (which has no cursor).
|
||
metadata:
|
||
tags: spring, click, physics, cursor, subtractive, interaction, synchronized
|
||
---
|
||
|
||
# Physics Press Reaction (Cursor + Element Synced)
|
||
|
||
Models a real click: a cursor approaches a button, lands, and both compress IN SYNC, then release together. Distinct from [press-release-spring.md](press-release-spring.md) (no cursor — just a press happening); this rule is the COMBINED cursor + element behavior. A single `PRESS_INTENSITY` drives both: press down compresses both to `1 - PRESS_INTENSITY` via **one targets array**, release springs both back to 1.0 with overshoot. The cursor translates to the button's center BEFORE the press starts; after release it may move on or hold.
|
||
|
||
## Recipe
|
||
|
||
```html
|
||
<button class="btn" id="btn">{ctaCopy}</button>
|
||
<!-- Cursor at scene-root level so it translates freely; arrow TIP is the click
|
||
point, so transform-origin: 0 0 — scaling around the tip keeps it stable. -->
|
||
<svg class="cursor" id="cursor" style="pointer-events: none; transform-origin: 0 0">…</svg>
|
||
```
|
||
|
||
```js
|
||
gsap.set("#cursor", { x: CURSOR_START_X, y: CURSOR_START_Y }); // off-screen / far corner
|
||
|
||
// Phase 1 — approach
|
||
tl.to(
|
||
"#cursor",
|
||
{ x: BUTTON_CENTER_X, y: BUTTON_CENTER_Y, duration: APPROACH_DUR, ease: "power2.inOut" },
|
||
APPROACH_START,
|
||
);
|
||
|
||
// Phase 2 — coordinated press down: ONE targets array, same scale
|
||
tl.to(
|
||
["#btn", "#cursor"],
|
||
{ scale: 1 - PRESS_INTENSITY, duration: PRESS_DOWN_DUR, ease: "power1.in" },
|
||
PRESS_DOWN_AT,
|
||
);
|
||
|
||
// Phase 3 — release: both spring back together
|
||
tl.to(
|
||
["#btn", "#cursor"],
|
||
{ scale: 1, duration: RELEASE_DUR, ease: `back.out(${BOUNCE_FACTOR})` },
|
||
RELEASE_AT,
|
||
);
|
||
|
||
// Phase 4 — inner glow during press, resting shadow on release (contact confirmation)
|
||
tl.to(
|
||
"#btn",
|
||
{ boxShadow: "{btnPressedShadow}", duration: PRESS_DOWN_DUR, ease: "power1.in" },
|
||
PRESS_DOWN_AT,
|
||
);
|
||
tl.to(
|
||
"#btn",
|
||
{ boxShadow: "{btnRestingShadow}", duration: RELEASE_DUR, ease: "power2.out" },
|
||
RELEASE_AT,
|
||
);
|
||
|
||
// Cursor optionally exits after the press settles
|
||
tl.to(
|
||
"#cursor",
|
||
{ x: CURSOR_EXIT_X, y: CURSOR_EXIT_Y, duration: CURSOR_EXIT_DUR, ease: "power2.out" },
|
||
CURSOR_EXIT_AT,
|
||
);
|
||
```
|
||
|
||
## Variations
|
||
|
||
- **Multiple-element chain press** — press button A → A triggers a swap → cursor moves to button B → presses again; each press is one full down-release sub-routine.
|
||
- **Hold press (continuous pressure)** — insert a `HOLD_DUR` window between press-down and release: both scales stay at `1 - PRESS_INTENSITY`, inner glow stays on. Suggests "thinking" or "loading."
|
||
- **Synchronized inner-glow pulse** — during the hold, pulse the inset glow with a sine driver: a `{ p: 0 }` proxy tweened to `Math.PI * GLOW_PULSE_CYCLES * 2` on `ease: "none"`, `onUpdate` writing `boxShadow` with `alpha = GLOW_BASE_ALPHA + sin(p) * GLOW_PULSE_AMP`. Suggests "processing."
|
||
|
||
## Values
|
||
|
||
| token | range / rule | notes |
|
||
| ------------------- | ---------------------------------------- | -------------------------------------------------------------------------------------- |
|
||
| APPROACH_START | 0–0.3 s | long delays read as a dead frame |
|
||
| APPROACH_DUR | 0.7–1.3 s | faster = urgent, slower = deliberate |
|
||
| PRESS_DOWN_AT | `= APPROACH_START + APPROACH_DUR` | cursor arrives exactly as the press begins — avoids "tapping on air" |
|
||
| PRESS_DOWN_DUR | 0.1–0.25 s | |
|
||
| RELEASE_AT | > `PRESS_DOWN_AT + PRESS_DOWN_DUR` | optional 0.05–0.4 s hold (or `HOLD_DUR` 0.3–0.8 s) for "thinking" interactions |
|
||
| RELEASE_DUR | 0.4–0.7 s | long enough for the overshoot to settle |
|
||
| PRESS_INTENSITY | 0.05 subtle · 0.10 standard · 0.15 heavy | applied to both cursor and button via the single targets array |
|
||
| BOUNCE_FACTOR | 1.6 soft · 2.0 firm · 2.4 cartoony | |
|
||
| CURSOR_START / EXIT | off-screen or far corner | the approach must read as motion-in, not a teleport; exit ≥ `RELEASE_AT + RELEASE_DUR` |
|
||
| BUTTON_CENTER | measured | for `place-items: center` at 1920×1080: `(960, 540)` |
|
||
| BRAND_REVEAL_AT | < `PRESS_DOWN_AT` | context precedes interaction |
|
||
| glow pulse | 1–4 cycles; base α 0.15–0.3; amp 0.1–0.2 | `GLOW_BASE_ALPHA − GLOW_PULSE_AMP ≥ 0` |
|
||
| CURSOR_SIZE | 48–96 px at 1080p | |
|
||
|
||
## Critical Constraints
|
||
|
||
- **Same press scale on cursor AND button** (one targets array) — only the button scaling makes the cursor "tap on air"; only the cursor scaling makes the button feel disconnected.
|
||
- **Cursor arrives BEFORE the press starts** — a clear "cursor over target" moment, or the press is unattributed.
|
||
- **`back.out(BOUNCE_FACTOR)` on the release, for both together** — a linear release loses the tactile feel; release MUST come after press.
|
||
- **Inner glow appears DURING press, fades on release** — outer shadow shrinks (pushed in), inner glow appears (energy concentrated).
|
||
- **Cursor `transform-origin: 0 0`** — the arrow's tip is the click point; scale around the tip keeps it stable. `pointer-events: none` on the cursor.
|
||
- **Climax dwell ≥ 1 s** — after release the composition must continue ≥ 1 s; the press is a beat, the viewer needs time to see the result.
|
||
- **No real `mouseenter` / `click` events** — HF is a render context; everything runs via the timeline.
|
||
|
||
## See also
|
||
|
||
`press-release-spring` (the BUTTON-only press; this rule layers the cursor on top) · `cursor-click-ripple` (adds a ripple at the click point) · `scale-swap-transition` (the press TRIGGERS the swap).
|