Files
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

7.6 KiB
Raw Permalink Blame History

name, description, metadata
name description metadata
multi-phase-camera Sequential camera zoom with 2-3 distinct phases (pull-back / focus / push) plus continuous micro-drift for organic cinematic feel.
tags
camera, zoom, phase, drift, scale, cinematic

Multi-Phase Camera

A camera wrapper around the ENTIRE scene that progresses through discrete zoom phases at scripted triggers, with continuous sine-driven micro-drift overlaid so the camera never feels static between phases. Distinct from a single linear zoom — multi-phase creates cinematic pacing (anticipation → reveal → settle).

How It Works

The camera is one wrapping <div> whose transform: scale() translate(x, y) is composed from two channels inside a single onUpdate writer:

  1. Phase scale — a proxy object { scale } stepped through phases at trigger times (PHASE_1_SCALE at t=0 → PHASE_2_SCALE at PHASE_2_ATPHASE_3_SCALE at PHASE_3_AT).
  2. Drift offset — a continuous sine-based translateX / translateY (small amplitude, slow frequency) ADDED to the phase transform. X and Y run at slightly different frequencies (DRIFT_FREQ_RATIO ≈ 1.3) — equal frequencies produce a perfect diagonal that reads mechanical; ~1.3 gives an organic Lissajous.

Recipe

<div class="camera" id="camera">
  <div class="content">
    <div class="hero">{Brand}</div>
    <div class="tagline">{tagline}</div>
    <div class="cta">{ctaText}</div>
  </div>
</div>
.scene {
  overflow: hidden; /* REQUIRED — any phase scale < 1 exposes the content's edges */
  background: {sceneBgColor}; /* background on .scene, NOT .camera — a camera-borne
     background warps/translates with the transform and reveals the outer void */
}
.camera {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  transform-origin: 50% 50%; /* off-center origin creates phase-to-phase drift */
  will-change: transform;
}
const camera = document.getElementById("camera");

// Three-phase scale plan: pullback → focus → push.
const phase = { scale: PHASE_1_SCALE }; // Phase 1 is the initial value — no tween

// Phase 2 — settle to neutral focus
tl.to(phase, { scale: PHASE_2_SCALE, duration: PHASE_2_DUR, ease: PHASE_2_EASE }, PHASE_2_AT);

// Phase 3 — slow push-in for the climax
tl.to(phase, { scale: PHASE_3_SCALE, duration: PHASE_3_DUR, ease: PHASE_3_EASE }, PHASE_3_AT);

// Drift driver — continuous sine motion overlaid on the phase scale.
// The ONE writer of camera.style.transform.
const drift = { p: 0 };
tl.to(
  drift,
  {
    p: Math.PI * 2 * DRIFT_CYCLES,
    duration: TOTAL_DURATION, // spans the whole composition
    ease: "none",
    onUpdate: () => {
      const dx = Math.sin(drift.p) * DRIFT_AMP_X;
      const dy = Math.sin(drift.p * DRIFT_FREQ_RATIO) * DRIFT_AMP_Y;
      camera.style.transform = `scale(${phase.scale}) translate(${dx}px, ${dy}px)`;
    },
  },
  0,
);

// Content reveals happen INSIDE the camera frame (hero/tagline/cta beats).

Phase Patterns

Pattern Scale sequence (1 → 2 → 3) Feel When to use
Focus-in back → neutral → slight push Approach → settle → slight push Default product reveal
Dramatic reveal push → neutral → pull Wide → focus → settle back Hero shot with breathing room
Steady push neutral → slight push → more push Gradual forward momentum Continuous narrative push
Bookend pull neutral → strong push → neutral Settle → push → release CTA emphasis then release

Variations

  • Phase trigger by content beat: align a camera tween's start with a content tween's end (entry completes → push begins) rather than a fixed clock value.
  • Camera shake (panic / impact): a brief higher-amplitude, higher-frequency drift tween over a short window — same drift mechanism with SHAKE_AMP / SHAKE_CYCLES / SHAKE_DUR at SHAKE_AT.
  • Targeted zoom into an off-center element: combine scale with counter-translation so the target lands at viewport center — divide the measured offset by the current scale before feeding it into the writer:
const tRect = document.querySelector(".cta").getBoundingClientRect();
const offsetX = (STAGE_W / 2 - (tRect.left + tRect.width / 2)) / phase.scale;
const offsetY = (STAGE_H / 2 - (tRect.top + tRect.height / 2)) / phase.scale;
// then in onUpdate: translate(offsetX + dx, offsetY + dy)

(Full counter-translate doctrine: coordinate-target-zoom.md.)

Values

token range notes
PHASE_1 / 2 / 3_SCALE 0.880.96 / 0.981.02 / 1.041.15 tighter spread = subtler camera; scale < 1 REQUIRES overflow: hidden on .scene
PHASE_2_AT / PHASE_2_DUR 0.31.0s / 1.01.8s longer DUR = slower settle, more cinematic
PHASE_3_AT / PHASE_3_DUR 2.04.0s / 1.02.0s PHASE_3_AT ≥ PHASE_2_AT + PHASE_2_DUR or focus is preempted
PHASE_2_EASE / PHASE_3_EASE power2.out power3.out power2.inOut spring/back easing on a camera feels uncomfortable; each later phase settles deeper
TOTAL_DURATION = data-duration the drift tween must span the whole composition
DRIFT_CYCLES 13 1 = one slow breath; high values read as mechanical wobble
DRIFT_AMP_X / DRIFT_AMP_Y 28 px / 14 px imperceptible per-frame, visible over time — if it reads as a shake, it's too much
DRIFT_FREQ_RATIO 1.21.5 1.0 = perfect diagonal (mechanical); ~1.3 = organic Lissajous
HERO_AT (etc.) after Phase-2 settle lands a hero fading in mid-pull-back feels like it's flying away

Critical Constraints

  • Camera wraps EVERYTHING in the scene — a per-element camera creates parallax bugs and breaks the "one viewpoint" read.
  • One writer: phase scale and drift compose inside the single drift onUpdate; nothing else touches camera.style.transform.
  • overflow: hidden on .scene — required whenever any phase scale < 1.
  • transform-origin: 50% 50% on .camera — off-center origin creates unpredictable phase-to-phase drift.
  • Scene background on .scene, not .camera — otherwise scaling/translating reveals the outer void.
  • Hero reveal starts AFTER the initial pull-back ease lands — otherwise the headline feels like it's flying away.

See also

coordinate-target-zoom.md (counter-translate math for the targeted variation) · orbit-3d-entry.md (orbit inside a drifting camera) · counting-dynamic-scale.md (climax push synced to counter peak) · 3d-text-depth-layers.md (depth-stacked hero under camera moves) · sine-wave-loop.md (element idle inside the camera).