"use client"; import { useRef, useLayoutEffect, useEffect, useState } from "react"; import gsap from "gsap"; import { ScrollTrigger } from "gsap/ScrollTrigger"; // Register GSAP plugins if (typeof window !== "undefined") { gsap.registerPlugin(ScrollTrigger); } interface TimelineItem { type: "education" | "experience"; title: string; organization: string; period: string; description: string; skills?: string[]; } const timelineData: TimelineItem[] = [ { type: "experience", title: "Senior Frontend Developer", organization: "Tech Innovators Inc.", period: "2023 - Present", description: "Leading the frontend development team, architecting scalable solutions, and implementing cutting-edge web technologies for enterprise clients.", skills: ["React", "Next.js", "TypeScript", "GSAP"], }, { type: "education", title: "Master's in Computer Science", organization: "Stanford University", period: "2021 - 2023", description: "Specialized in Human-Computer Interaction and Web Technologies. Thesis focused on immersive web experiences and motion design.", }, { type: "experience", title: "Frontend Developer", organization: "Digital Agency Co.", period: "2020 - 2023", description: "Built responsive and interactive web applications for diverse clients. Specialized in animation and performance optimization.", skills: ["JavaScript", "Vue.js", "CSS3", "Three.js"], }, { type: "education", title: "Bachelor's in Software Engineering", organization: "MIT", period: "2016 - 2020", description: "Strong foundation in computer science fundamentals, algorithms, and software development methodologies.", }, { type: "experience", title: "Junior Web Developer", organization: "StartUp Hub", period: "2019 - 2020", description: "Started my professional journey building websites and learning modern development practices in a fast-paced startup environment.", skills: ["HTML5", "CSS3", "JavaScript", "React"], }, ]; export default function TimelineSection() { const sectionRef = useRef(null); const timelineRef = useRef(null); const progressLineRef = useRef(null); const curvePathRef = useRef(null); const [pathLength, setPathLength] = useState(0); const [mounted, setMounted] = useState(false); // Set mounted state after hydration useEffect(() => { setMounted(true); }, []); // Calculate path length on mount useEffect(() => { if (curvePathRef.current) { setPathLength(curvePathRef.current.getTotalLength()); } }, [mounted]); useLayoutEffect(() => { if (!mounted) return; const isMobile = window.innerWidth < 768; const ctx = gsap.context(() => { // Animate the section header gsap.fromTo( ".timeline-header", { opacity: 0, y: 40, }, { opacity: 1, y: 0, duration: 1, ease: "power3.out", scrollTrigger: { trigger: sectionRef.current, start: "top 85%", end: "top 60%", scrub: 1, }, } ); // Animate the straight progress line gsap.fromTo( progressLineRef.current, { scaleY: 0, transformOrigin: "top center", }, { scaleY: 1, ease: "none", scrollTrigger: { trigger: timelineRef.current, start: "top 80%", end: "bottom 20%", scrub: 0.5, }, } ); // Animate the curved background strip (only on desktop) if (pathLength > 0 && !isMobile) { gsap.fromTo( curvePathRef.current, { strokeDashoffset: pathLength, }, { strokeDashoffset: 0, ease: "none", scrollTrigger: { trigger: sectionRef.current, start: "top 80%", end: "bottom 20%", scrub: 0.5, }, } ); } // Animate timeline items const items = gsap.utils.toArray(".timeline-item"); items.forEach((item) => { // Card animation gsap.fromTo( item.querySelector(".timeline-card"), { opacity: 0, y: 40, scale: 0.95, }, { opacity: 1, y: 0, scale: 1, duration: 1, ease: "power3.out", scrollTrigger: { trigger: item, start: "top 90%", end: "top 60%", scrub: 1, }, } ); // Dot animation gsap.fromTo( item.querySelector(".timeline-dot"), { scale: 0, opacity: 0, }, { scale: 1, opacity: 1, duration: 0.5, ease: "back.out(2)", scrollTrigger: { trigger: item, start: "top 85%", end: "top 65%", scrub: 1, }, } ); // Content stagger animation gsap.fromTo( item.querySelectorAll(".timeline-content > *"), { opacity: 0, y: 15, }, { opacity: 1, y: 0, duration: 0.6, stagger: 0.08, ease: "power2.out", scrollTrigger: { trigger: item, start: "top 80%", end: "top 50%", scrub: 1, }, } ); }); }, sectionRef); return () => ctx.revert(); }, [pathLength, mounted]); return (
{/* Background Curved Grey Strip - Hidden on mobile via CSS */}
{/* Section Header */}

My Journey

Education & Experience

{/* Timeline */}
{/* Straight Vertical Line - Background */}
{/* Straight Vertical Line - Progress */}
{/* Timeline Items */}
{timelineData.map((item, index) => (
{/* Card */}
{/* Type Badge */} {item.type === "education" ? "📚 Education" : "💼 Experience"} {/* Period */}

{item.period}

{/* Title */}

{item.title}

{/* Organization */}

{item.organization}

{/* Description */}

{item.description}

{/* Skills */} {item.skills && (
{item.skills.map((skill) => ( {skill} ))}
)}
{/* Center Dot */}
{/* Empty space for alternating layout (desktop only) */}
))}
); }