Agent_PDF / web /src /components /LandingAnimation.jsx
MohamedSameh77i's picture
Deploy Agent_PDF upload UI improvements
563ed2b verified
import { useEffect, useRef, useState } from "react";
import { useAnimate } from "motion/react";
import { twMerge } from "tailwind-merge";
import { clsx } from "clsx";
function cn(...inputs) {
return twMerge(clsx(inputs));
}
export const FedoraSVG = ({ className }) => (
<svg viewBox="0 0 100 60" className={cn("w-full h-full drop-shadow-[0_8px_16px_rgba(0,0,0,0.6)]", className)} xmlns="http://www.w3.org/2000/svg">
<path d="M 28 40 L 35 15 C 37 5, 45 5, 50 10 C 55 15, 63 5, 65 10 C 70 15, 72 30, 72 40 Z" fill="#5c4033" stroke="#2a1b14" strokeWidth="2" strokeLinejoin="round" />
<path d="M 28 40 L 31 30 Q 52 38 69 30 L 72 40 Q 52 45 28 40 Z" fill="#111111" />
<path d="M 4 45 C 4 45, 25 35, 50 42 C 75 48, 96 38, 96 38 C 96 38, 90 50, 50 50 C 15 50, 4 45, 4 45 Z" fill="#463025" stroke="#2a1b14" strokeWidth="2" strokeLinejoin="round"/>
</svg>
);
// Custom Geometric SVGs
const LetterA = ({ className, ...props }) => (
<svg viewBox="0 0 100 100" className={cn("inline-block w-[0.8em] h-[1em]", className)} {...props} fill="currentColor">
<rect x="10" y="10" width="20" height="80" />
<rect x="70" y="10" width="20" height="80" />
<rect x="10" y="10" width="80" height="20" />
<rect x="10" y="45" width="80" height="20" />
</svg>
);
const LetterP = ({ className, ...props }) => (
<svg viewBox="0 0 100 100" className={cn("inline-block w-[0.8em] h-[1em]", className)} {...props} fill="currentColor">
<rect x="10" y="10" width="20" height="80" />
<rect x="70" y="10" width="20" height="55" />
<rect x="10" y="10" width="80" height="20" />
<rect x="10" y="45" width="80" height="20" />
</svg>
);
const LetterD = ({ className, ...props }) => (
<svg viewBox="0 0 100 100" className={cn("inline-block w-[0.8em] h-[1em]", className)} {...props} fill="currentColor">
<rect x="10" y="10" width="20" height="80" />
<rect x="70" y="10" width="20" height="80" />
<rect x="10" y="10" width="80" height="20" />
<rect x="10" y="70" width="80" height="20" />
</svg>
);
const LetterF = ({ className, ...props }) => (
<svg viewBox="0 0 100 100" className={cn("inline-block w-[0.8em] h-[1em]", className)} {...props} fill="currentColor">
<rect x="10" y="10" width="20" height="80" />
<rect x="10" y="10" width="80" height="20" />
<rect x="10" y="45" width="60" height="20" />
</svg>
);
export function LandingAnimation({ onComplete }) {
const [scope, animate] = useAnimate();
const aRef = useRef(null);
const gentRef = useRef(null);
const pRef = useRef(null);
const dRef = useRef(null);
const fRef = useRef(null);
const [phase, setPhase] = useState(0);
useEffect(() => {
let active = true;
const runSeq = async () => {
// Wait for layout/fonts to mount
await new Promise(r => setTimeout(r, 400));
if (!active) return;
const fedoraNode = document.getElementById("fedora");
const aRect = aRef.current?.getBoundingClientRect();
const fRect = fRef.current?.getBoundingClientRect();
if (fedoraNode && aRect && fRect) {
const targetRightX = fRect.right - aRect.left + 20;
const startY = fRect.height * 0.4;
// Snap Fedora directly to the right side of Agent PDF initially
await animate(fedoraNode, { x: targetRightX, y: startY, rotate: 10 }, { duration: 0 });
await animate(fedoraNode, { opacity: 1 }, { duration: 0.3 });
await new Promise(r => setTimeout(r, 400));
if (!active) return;
// 1. Hat Boomerang to right (fly out further)
await animate(fedoraNode, { x: targetRightX + aRect.width * 1.5, y: startY - aRect.height * 0.2, rotate: 30 }, { duration: 0.6, ease: "easeOut" });
// 2. Hat loops back to lands perfectly on 'A' Base coordinates (0, 0)
await animate(fedoraNode, { x: 5, y: -aRect.height * 0.05, rotate: -5, scale: 0.95 }, { duration: 0.7, ease: "backOut" });
setPhase(1); // Hat landed
await new Promise(r => setTimeout(r, 400));
if (!active) return;
// 3. 'gent' collapses
if (gentRef.current) {
const gentWidth = gentRef.current.offsetWidth;
gentRef.current.style.width = `${gentWidth}px`;
await animate(gentRef.current, { width: 0, opacity: 0, paddingRight: 0, marginLeft: 0 }, { duration: 0.6, ease: "backIn" });
gentRef.current.style.display = 'none';
}
// Collapse spacer
await animate("#spacer", { width: 0 }, { duration: 0.2 });
// 4. P, D, F merge into A
const currentARect2 = aRef.current?.getBoundingClientRect();
const mergeLetter = async (ref, delay) => {
if (!ref.current || !currentARect2) return;
const rect = ref.current.getBoundingClientRect();
const offsetX = currentARect2.left - rect.left;
animate(ref.current, { x: offsetX, opacity: 0, filter: "blur(4px)" }, { duration: 0.5, delay });
};
mergeLetter(pRef, 0);
mergeLetter(dRef, 0.15);
await mergeLetter(fRef, 0.3);
if (!active) return;
// 5. Center the block! We slide #text-container.
const scopeRect = scope.current?.getBoundingClientRect();
const finalARect = aRef.current?.getBoundingClientRect();
if (scopeRect && finalARect) {
const aCenterX = finalARect.left + finalARect.width / 2;
const scopeCenterX = scopeRect.left + scopeRect.width / 2;
const offset = scopeCenterX - aCenterX;
await animate("#text-container", { x: offset }, { duration: 0.8, ease: "easeInOut" });
}
setPhase(2); // Merge & Center complete
// Notify parent that animation is complete
if (onComplete) onComplete();
}
};
runSeq();
return () => { active = false; };
}, [animate, onComplete, scope]);
return (
<div ref={scope} className="relative select-none flex items-center justify-center min-h-[300px] w-full mt-10">
<div id="text-container" className="flex items-end text-[80px] md:text-[120px] font-black leading-none tracking-tighter text-slate-800 relative">
{/* The 'A' and its child Fedora! */}
<span ref={aRef} className={cn("relative z-20 flex transition-all duration-700 ease-in-out", phase >= 2 && "text-orange-500 drop-shadow-[0_0_20px_rgba(229,115,0,0.6)] px-2")}>
<LetterA />
{/* The Fedora securely contained within A, initially invisible */}
<div
id="fedora"
className="absolute bottom-[90%] left-[-8%] w-[1.3em] z-30 transform origin-bottom opacity-0"
>
<FedoraSVG />
</div>
</span>
{/* 'gent' (Standard font for contrast) */}
<span ref={gentRef} className="relative z-10 overflow-hidden text-slate-600 whitespace-nowrap pl-[0.05em] origin-left pb-2 text-[60px] md:text-[90px]" style={{ fontFamily: "ui-sans-serif, system-ui, sans-serif" }}>
gent
</span>
{/* Space */}
<span className="w-4 md:w-6 inline-block" id="spacer"></span>
{/* P, D, F */}
<span ref={pRef} className="relative z-10 text-slate-700 flex"><LetterP /></span>
<span ref={dRef} className="relative z-10 text-slate-600 flex"><LetterD /></span>
<span ref={fRef} className="relative z-10 text-slate-500 flex"><LetterF /></span>
</div>
</div>
);
}