// anim-prism.jsx — Variation 1: Prisma // A thin white beam enters from the left, hits a prism point, fans into the // visible spectrum. The magenta wavelength isolates and 3 beams of magenta // tones converge into the M. Tagline + breath loop. const PrismAnim = ({ t, duration }) => { // local helpers const clamp01 = (v) => Math.max(0, Math.min(1, v)); const easeOut = (x) => 1 - Math.pow(1 - x, 3); const easeIn = (x) => x * x * x; const easeIO = (x) => x < 0.5 ? 4 * x * x * x : 1 - Math.pow(-2 * x + 2, 3) / 2; const lerp = (a, b, x) => a + (b - a) * x; const seg = (t0, t1, te = easeIO) => te(clamp01((t - t0) / (t1 - t0))); // Phases (5.4s total — last 0.4s blends into loop start for seamless loop) const TBeamDraw = seg(0.10, 1.10, easeOut); // beam draws in const TPrismHit = seg(1.00, 1.40, easeOut); // prism dot flares const TFan = seg(1.20, 2.40, easeOut); // spectrum fans out const TIsolate = seg(2.30, 3.10, easeIO); // other colors fade, magenta isolates const TConverge = seg(2.80, 3.60, easeIO); // magenta beams converge to M position const TReveal = seg(3.40, 4.10, easeOut); // M materializes via wipe const TFlash = seg(3.90, 4.30, easeOut); // bright flash on lock const TTagline = seg(4.20, 4.80, easeOut); // tagline fades in const breath = 0.5 + 0.5 * Math.sin(t / duration * Math.PI * 2 * 1); // 1 cycle/loop const subtleBreath = 1 - 0.03 * Math.max(0, Math.sin((t - 4.5) * Math.PI / 0.8)); // Loop crossfade: last 0.35s fades opacity of whole pulsing piece into start const loopFade = clamp01((duration - t) / 0.35); // Geometry — centered on canvas const CX = 960,CY = 540; const M_W = 520, M_H = 283; // Use % + translate so centering is bulletproof regardless of parent layout quirks. // Vertical offset: pull up so M+tagline group reads as balanced around canvas center. const M_TOP_PCT = 38; // y center as % of stage height (38% → above middle) // Spectrum band colors (red→violet) with magentas added const bands = [ { h: '#ff2d4a', label: 'R' }, { h: '#ff7a1a', label: 'O' }, { h: '#ffd60a', label: 'Y' }, { h: '#34d399', label: 'G' }, { h: '#38bdf8', label: 'C' }, { h: '#6366f1', label: 'B' }, { h: '#a855f7', label: 'V' }]; // Beam parameters const beamStartX = -100; const beamEndX = CX - 6; // stops just before prism point const beamY = CY; const beamLen = lerp(0, beamEndX - beamStartX, TBeamDraw); const beamTipX = beamStartX + beamLen; // Prism dot scale / glow const prismScale = 0.2 + 1.6 * TPrismHit - 0.4 * TFan; // flares then settles const prismOpacity = clamp01(TPrismHit - TFan * 0.6); // Spectrum fan: each band is a long thin gradient at an angle const fanLen = lerp(0, 1600, TFan); const fanOpacity = lerp(0, 1, TFan) * (1 - TIsolate * 0.85); // Magenta isolation: 3 beams (the three M tones) converging on M strokes const magenta3 = [ { color: '#7a1b54', angle: -10, label: 'plum' }, // dark plum { color: '#c66689', angle: 0, label: 'pink' }, // mid pink { color: '#e6217a', angle: 10, label: 'hot' } // hot magenta ]; // M wipe progress — vertical sweep from bottom-up reveal using clip-path const wipeAmt = TReveal; // 0..1 // clip-path inset from top: visible portion grows from bottom up const wipeInset = (1 - wipeAmt) * 100; // Flash opacity (white fullscreen flash on lock) const flashOpacity = TFlash * (1 - TFlash * 0.85); // Final logo opacity & glow pulse (after reveal) const logoVisible = clamp01((t - 3.4) / 0.35); const finalPulse = t > 4.2 ? 0.85 + 0.15 * Math.sin((t - 4.2) * 2 * Math.PI / 1.4) : 0; return (
{/* Faint stars */} {/* Horizontal guide line (faint) — only during beam phase, fades after fan */}
{/* The white beam */}
{/* Beam tip highlight */} {TBeamDraw > 0 && TFan < 0.5 &&
} {/* The prism (small triangle / point) */}
{/* Spectrum fan — each band rotated and translated */}
{bands.map((b, i) => { const angle = -36 + i * 12; // -36° to +36° const isMagentaRange = i >= 5; // blue+violet wrap toward magenta const fade = TIsolate * (isMagentaRange ? 0.2 : 1.0); return (
); })}
{/* Magenta isolation — 3 converging beams of magenta tones */} {TIsolate > 0 &&
{magenta3.map((m, i) => { const startAngle = -36 + (i === 0 ? 0 : i === 1 ? 0 : 0); // start from fan angles const startA = [-20, 0, 20][i]; const targetA = [-15, 0, 15][i]; const angle = lerp(startA, targetA, TConverge); const len = lerp(900, 380, TConverge); // beams shorten as they reach the M return (
); })}
} {/* M Reveal — uses original logo image, wiped from bottom up */}
4.2 ? lerp(1, 1.015, (finalPulse - 0.85) / 0.15) : 1)})`, transformOrigin: 'center', clipPath: `inset(${wipeInset}% 0 0 0)`, WebkitClipPath: `inset(${wipeInset}% 0 0 0)` }}> M
{/* Bright flash on lock-in */}
{/* Tagline */} 4.2 ? finalPulse : 0} /> {/* Loop fade */}
); }; // Shared tagline component used across all variations // ───────────────────────────────────────────────────────────────────── // ✏️ EDIT TAGLINE TEXTS HERE (the words that appear under the M): // Main line: ALQUIMIA DE NEGOCIOS → buscar abajo "ALQUIMIA" // Subtitle: CONSULTORÍA · IA · ESTRATEGIA → buscar abajo "CONSULTORÍA" // ───────────────────────────────────────────────────────────────────── const Tagline = ({ opacity = 1, pulse = 0 }) => { const ty = (1 - opacity) * 12; return (
ALQUIMIA DE NEGOCIOS
CONSULTORÍA · IA · ESTRATEGIA
); }; // Faint starfield background, seeded so it doesn't flicker between re-renders const Stars = React.memo(({ seed = 1, count = 80 }) => { const stars = React.useMemo(() => { let s = seed * 12345; const rand = () => {s = (s * 9301 + 49297) % 233280;return s / 233280;}; return Array.from({ length: count }, () => ({ x: rand() * 1920, y: rand() * 1080, r: 0.5 + rand() * 1.4, o: 0.15 + rand() * 0.4, d: rand() * 4 })); }, [seed, count]); return (
{stars.map((s, i) =>
)}
); }); window.PrismAnim = PrismAnim; window.Tagline = Tagline; window.Stars = Stars;