Files
kittens.rip_bio/src/App.tsx
2026-05-18 22:26:10 +02:00

112 lines
3.2 KiB
TypeScript

import { useState, useEffect, useRef, useCallback } from 'react'
import Lenis from 'lenis'
import { gsap } from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'
import LoadingScreen from './components/LoadingScreen'
import Navigation from './components/Navigation'
import HeroSection from './components/HeroSection'
import AboutSection from './components/AboutSection'
import InterestsSection from './components/InterestsSection'
import ProjectsSection from './components/ProjectsSection'
import ExperienceSection from './components/ExperienceSection'
import TopicsSection from './components/TopicsSection'
import ContactSection from './components/ContactSection'
import Footer from './components/Footer'
import FloatingParticles from './components/FloatingParticles'
import ScrollProgress from './components/ScrollProgress'
import NoiseOverlay from './components/NoiseOverlay'
import { useReducedMotion } from './hooks/useReducedMotion'
gsap.registerPlugin(ScrollTrigger)
export default function App() {
const [loaded, setLoaded] = useState(false)
const [showContent, setShowContent] = useState(false)
const lenisRef = useRef<Lenis | null>(null)
const mainRef = useRef<HTMLElement>(null)
const capiAudioRef = useRef<HTMLAudioElement | null>(null)
const reducedMotion = useReducedMotion()
useEffect(() => {
capiAudioRef.current = new Audio('./media/capi.mp3')
capiAudioRef.current.preload = 'auto'
capiAudioRef.current.loop = true
capiAudioRef.current.volume = 0.25
}, [])
useEffect(() => {
if (!loaded) return
const lenis = new Lenis({
duration: 1.2,
easing: (t: number) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
smoothWheel: true,
})
lenisRef.current = lenis
lenis.on('scroll', ScrollTrigger.update)
const rafCallback = (time: number) => {
lenis.raf(time * 1000)
}
gsap.ticker.add(rafCallback)
gsap.ticker.lagSmoothing(0)
return () => {
gsap.ticker.remove(rafCallback)
lenis.destroy()
}
}, [loaded])
const handleLoadComplete = useCallback(() => {
setLoaded(true)
// Small delay before showing content for transition
setTimeout(() => {
setShowContent(true)
}, 100)
}, [])
useEffect(() => {
if (showContent) {
// Start ambient capi.mp3 loop
capiAudioRef.current?.play().catch(() => {})
}
}, [showContent])
useEffect(() => {
if (showContent && mainRef.current && !reducedMotion) {
gsap.fromTo(
mainRef.current,
{ opacity: 0, y: 40 },
{ opacity: 1, y: 0, duration: 1.2, ease: 'expo.out' }
)
}
}, [showContent, reducedMotion])
return (
<>
<LoadingScreen onLoadComplete={handleLoadComplete} />
{showContent && (
<>
<ScrollProgress />
<NoiseOverlay />
<FloatingParticles />
<Navigation />
<main ref={mainRef} className="relative" style={{ opacity: reducedMotion ? 1 : 0 }}>
<HeroSection />
<AboutSection />
<InterestsSection />
<ProjectsSection />
<ExperienceSection />
<TopicsSection />
<ContactSection />
<Footer />
</main>
</>
)}
</>
)
}