import { useCurrentFrame, useVideoConfig, interpolate } from 'remotion'; import { C } from '../lib/colors'; import { rand, clampLerp, easeInOut, softSpring } from '../lib/easings'; import { BEAT } from '../HeroVideo'; // Beat 2 — the wow moment. // // The prompt words detonate into ~30 particle dots. Each particle has // (a) a random scatter velocity that decays, (b) a magnetic pull toward // a designated target slot on the SERVER SCHEMATIC that materialises in // the centre. As particles arrive, the schematic strokes itself on // line-by-line. A scan-line sweeps over the formed server right before // it settles. const PARTICLE_COUNT = 36; // Target slots on the server schematic — points on the rectangle's // perimeter plus internal "row" markers. Particles slot into these. const SERVER_W = 460; const SERVER_H = 300; const CX = 960; const CY = 540; function targetSlot(i: number) { const N = PARTICLE_COUNT; // Distribute slots: half on the perimeter, half on internal "tool rows". if (i < N / 2) { // perimeter — walk around the rectangle const t = i / (N / 2); const perim = 2 * (SERVER_W + SERVER_H); const d = t * perim; const left = CX - SERVER_W / 2; const top = CY - SERVER_H / 2; let px = left; let py = top; if (d < SERVER_W) { px = left + d; py = top; } else if (d < SERVER_W + SERVER_H) { px = left + SERVER_W; py = top + (d - SERVER_W); } else if (d < 2 * SERVER_W + SERVER_H) { px = left + SERVER_W - (d - SERVER_W - SERVER_H); py = top + SERVER_H; } else { px = left; py = top + SERVER_H - (d - 2 * SERVER_W - SERVER_H); } return { x: px, y: py }; } // Internal tool rows — three horizontal lines inside the server const j = i - N / 2; const row = j % 3; const col = Math.floor(j / 3) / (N / 2 / 3 - 1); const rowY = CY - 60 + row * 60; const rowX = CX - SERVER_W / 2 + 30 + col * (SERVER_W - 60); return { x: rowX, y: rowY }; } export function TransformScene() { const frame = useCurrentFrame(); const { fps } = useVideoConfig(); // Local frame within the beat — frame 0 is the start of Transform. const local = frame - BEAT.transform.in; // 0..110 const total = BEAT.transform.out - BEAT.transform.in; // Entrance + exit envelopes for the scene as a whole. const sceneIn = clampLerp(frame, BEAT.transform.in, BEAT.transform.in + 6); const sceneOut = 1 - clampLerp(frame, BEAT.transform.out - 8, BEAT.transform.out); const sceneAlpha = Math.min(sceneIn, sceneOut); // Schematic stroke-on: 0→1 between frames 35..65 of the beat. const strokeT = easeInOut(clampLerp(local, 30, 70)); // Port pulse readiness — only after schematic strokes complete. const portPulse = clampLerp(local, 70, 95); // Scan-line sweep — diagonal gradient passes once across the formed server const scanT = clampLerp(local, 70, 100); return (