// counter-confetti — Counter Confetti digit sprint confetti (the motion-lab final ported to // native Remotion) // The big number sprints 0 → 1000 with easeOutQuart, scale following an [0.2,1.3,1.3,1] overshoot; // 5 frames before the number lands, 8-color confetti bursts from both sides, falling under gravity // while spinning and drifting. // Design coordinates are 480×270 (DesignStage scales up uniformly); the parameter table is // calibrated in this coordinate system. import React from 'react'; import { DesignStage, E, lerp, rand, seg, useT } from '../../_fixtures/Motion'; export const COUNTER_CONFETTI_DURATION = 138; // 4600ms @30fps const PAL = ['#ff6b8b', '#ffb347', '#ffe86b', '#7bff9e', '#5fd8ff', '#6c8cff', '#c86cff', '#ff6cf0']; const BURST = 0.52; // "pre-fires" before the count lands (0.56) // static params for 52 confetti pieces (seeds identical to effect.js, deterministically reproducible) const BITS = Array.from({ length: 52 }, (_, i) => { const isRect = rand(i * 3) > 0.4; const w = 5 + rand(i + 2) * 6; const h = isRect ? 8 + rand(i + 5) * 7 : w; const side = i % 2 ? 1 : -1; return { w, h, isRect, color: PAL[i % 8], x0: side * 250, // starts from both sides of the frame (px, relative to center) y0: (rand(i + 30) - 0.5) * 60, vx: -side * (150 + rand(i * 5 + 1) * 300), // rush toward the center of the frame vy: -(230 + rand(i * 7 + 3) * 220), g: 900 + rand(i + 60) * 520, spin: (rand(i + 90) - 0.5) * 1500, d: rand(i + 120) * 0.06, // each piece slightly staggered }; }); export const CounterConfetti: React.FC = () => { const t = useT(); // count: easeOutQuart sprint const p = seg(t, 0.06, 0.56, E.outQuart); const val = Math.round(p * 1000); // scale overshoot [0.2,1.3,1.3,1] const s1 = seg(t, 0.06, 0.3, E.outCubic); const s2 = seg(t, 0.56, 0.72, E.outBack); const sc = lerp(s1, 0.2, 1.3) + s2 * (1 - 1.3); // landing impact ring const rp = seg(t, 0.545, 0.75, E.outQuart); return ( {/* central glow */}
{/* big number (gradient-filled text, outer scale overshoot) */}
{val.toLocaleString('en-US')}
{/* bottom label: fade in + letter-spacing tightening */}
METRIC THIS WEEK
{/* landing impact ring */}
0 ? (1 - rp) * 0.9 : 0, transform: `scale(${0.35 + rp * 2.6})`, }} /> {/* confetti: pre-fire burst + gravity fall + self-rotation */} {BITS.map((b, i) => { const u = seg(t, BURST + b.d, 1); const life = u * 1.1; // second-scale time base const x = b.x0 + b.vx * life; const y = b.y0 + b.vy * life + 0.5 * b.g * life * life; return (
); })} ); };