Files
hyperframes/skills/hyperframes-animation/rules/cursor-click-ripple.md
T
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

105 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: cursor-click-ripple
description: Animated mouse cursor moves to target, clicks with scale depression and expanding ripple rings.
metadata:
tags: cursor, click, ripple, interaction, mouse, button
---
# Cursor Click Ripple
An animated cursor moves to a target element, performs a click with visual depression, and emits expanding ripple rings from the click point. Three sequential phases on one timeline: **move** (eased translation to the target's center) → **click** (scale depression on cursor + target together, yoyo back) → **ripple** (13 staggered rings expand and fade from the click point). This is a _point event at one location_ — a sustained hold across space is [cursor-drag.md](cursor-drag.md).
## Recipe
```html
<button class="target-button">{ctaLabel}</button>
<div class="cursor"><!-- arrow SVG, positioned at the entry corner --></div>
<!-- Rings live in DOM from t=0 at the click-target CENTER, scale 0 + opacity 0 -->
<div class="ripple ripple-1"></div>
<div class="ripple ripple-2"></div>
<div class="ripple ripple-3"></div>
```
```css
.ripple {
position: absolute;
left: 50%;
top: 50%; /* click-target center */
width: 100px;
height: 100px;
border-radius: 50%;
border: 2px solid {rippleColor};
transform: translate(-50%, -50%) scale(0);
opacity: 0;
pointer-events: none;
}
```
```js
// Phase 1 — Move: eased, not linear
tl.to(".cursor", { x: TARGET_X, y: TARGET_Y, duration: MOVE_DUR, ease: MOVE_EASE }, 0);
// Phase 2 — Click: cursor + target depress together, then return
tl.to(
".cursor",
{ scale: CURSOR_PRESS_SCALE, duration: PRESS_DUR, ease: "power2.in", yoyo: true, repeat: 1 },
CLICK_AT,
);
tl.to(
".target-button",
{ scale: TARGET_PRESS_SCALE, duration: PRESS_DUR, ease: "power2.in", yoyo: true, repeat: 1 },
CLICK_AT,
);
// Phase 3 — Ripple burst, N rings staggered from the click point
tl.set([".ripple-1", ".ripple-2", ".ripple-3"], { opacity: 1 }, RIPPLE_AT);
tl.to(
[".ripple-1", ".ripple-2", ".ripple-3"],
{
scale: RIPPLE_SCALE,
opacity: 0,
duration: RIPPLE_DUR,
ease: RIPPLE_EASE,
stagger: RIPPLE_STAGGER,
immediateRender: false, // holds scale 0 / opacity 0 until the click moment
},
RIPPLE_AT,
);
```
## Variations
- **Single ring** — one `.ripple`, no stagger; more elegant when the rest of the scene is busy.
- **Keyframed attack-decay** — a `keyframes` block ramps opacity 0 → peak → 0 across the duration; a clearer "energy radiates and dissipates" envelope.
- **Multi-ring expanding pulse** — 3 rings at 0.08 s stagger when the click is the scene's climactic moment.
## Values
| token | range | notes |
| --------------------------- | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| MOVE_DUR | 0.41.0 s | short darts; long reads as a "considered click." Must end before CLICK_AT or it reads as a misclick |
| MOVE_EASE | discrete choice | `power2.inOut` calm · `power3.out` decisive · `back.out(1.21.4)` settles onto the button with a tiny recoil (higher reads cartoonish) |
| CLICK_AT | `MOVE_DUR + 00.3 s` | zero pause reads as autopilot; >0.3 s reads as hesitation |
| PRESS_DUR | 0.060.12 s (half; yoyo ×2) | short crisp, long mushy; must finish before the next phase needs normal scale |
| CURSOR / TARGET_PRESS_SCALE | 0.800.90 / 0.920.97 | cursor compresses MORE than the target — the cursor is the actor, the target the recipient |
| RIPPLE_AT | `CLICK_AT + 00.08 s` | simultaneous feels causal; slight delay feels acoustic |
| RIPPLE_DUR | 0.51.0 s | sharp ping vs soft sonar; must complete before anything that needs the ring gone |
| RIPPLE_SCALE | 36 | 3 stays near the click site; if the ring would exit the frame before fading, lower it |
| RIPPLE_STAGGER | 0.060.12 s (or 0) | below ~0.06 s reads as one thick ring; above ~0.12 s as separate events |
| RIPPLE_EASE | discrete choice | `power2.out` standard ping · `power3.out` sharper attack · `expo.out` strong distant pulse |
| TARGET_X / TARGET_Y | layout-derived | must match the target's visual centroid — a 4 px miss reads as missing the button |
Reference values: `../../examples/cta-orbit-collapse.html` — 0.5 s move on `back.out(1.3)`, click +0.2 s, press 0.08 s at 0.85/0.95, single ring to 5× over 0.7 s `power2.out`.
## Critical Constraints
- **Move before click** — trigger the click only after the move tween settles; clicking mid-motion reads as unintentional.
- **Rings live in DOM from t=0** at the click-target center with `scale: 0` + `opacity: 0` — never conditionally rendered; `immediateRender: false` on the expand so they hold invisible until the trigger.
- **Ripple from the click point** — the button's visual center, not any element's bounding-box origin.
- **Synchronized depression** — cursor + target depress at the same position with the same duration, and both yoyo back.
- **Cursor above all content** (high z-index) for the whole sequence; `pointer-events: none` on cursor + ripples.
## See also
`orbit-3d-entry` (click as the pivot that collapses orbiters) · `center-outward-expansion` (click triggers an outward burst) · `press-release-spring` (stronger physical feel on the target) · `scale-swap-transition` (the button's post-click state change).