feat(hero): hero redesenhada, titulo visivel no HTML (LCP) e cena 3D so depois do idle
- titulo fluido (clamp ate 88px) com destaque em 'sob medida', eyebrow mono, seta no CTA e brilho de fundo - numeros reais na hero (anos de experiencia, projetos publicados, demos online) calculados em app/page.tsx a partir dos dados do portfolio - remove o opacity:0 inicial do motion: o texto nasce visivel; a entrada anima so a posicao (CSS) - HeroSystem monta a cena Three.js via requestIdleCallback (timeout 2,5s), depois de pintar e hidratar o essencial - capas decorativas (sem captura) escondidas no celular Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
This commit is contained in:
1 parent
35a950a9b1
commit
23aeaa2fda
5 files changed
+140
-28
No files matched your search
@@ -10,7 +10,7 @@ import Contact from "../Contact";
|
||||
import EngineeringWithAI from "../EngineeringWithAI";
|
||||
import Experience from "../Experience";
|
||||
import Footer from "../Footer";
|
||||
import Hero from "../Hero";
|
||||
import Hero, { type HeroStats } from "../Hero";
|
||||
import LighthouseMetrics from "../LighthouseMetrics";
|
||||
import Projects from "../Projects";
|
||||
import Skills from "../Skills";
|
||||
@@ -25,7 +25,9 @@ export default function CinematicHome({
|
||||
projects,
|
||||
experiences,
|
||||
skillCategories,
|
||||
heroStats,
|
||||
}: {
|
||||
heroStats?: HeroStats;
|
||||
projects: PortfolioProjectView[];
|
||||
experiences: PortfolioExperience[];
|
||||
skillCategories: PortfolioSkillCategory[];
|
||||
@@ -35,7 +37,7 @@ export default function CinematicHome({
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col overflow-x-clip">
|
||||
<main className="ds-main">
|
||||
<Hero />
|
||||
<Hero stats={heroStats} />
|
||||
|
||||
<div className="overflow-hidden bg-[var(--ink)] py-6 text-[rgba(244,242,237,0.06)]">
|
||||
<ScrollVelocityText text="SOFTWARE SOB MEDIDA" direction="left" />
|
||||
|
||||
@@ -88,11 +88,36 @@ export default function HeroSystem() {
|
||||
}
|
||||
}
|
||||
}
|
||||
setCanRender(supported);
|
||||
mountScene(supported);
|
||||
};
|
||||
// A cena 3D (Three.js) so entra depois que a pagina ficou ociosa: titulo, imagem e botoes pintam e hidratam primeiro.
|
||||
let idle: number | undefined;
|
||||
let timer: ReturnType<typeof setTimeout> | undefined;
|
||||
const cancelPending = () => {
|
||||
if (idle !== undefined) window.cancelIdleCallback?.(idle);
|
||||
if (timer !== undefined) clearTimeout(timer);
|
||||
idle = timer = undefined;
|
||||
};
|
||||
const mountScene = (value: boolean) => {
|
||||
cancelPending();
|
||||
if (!value) {
|
||||
setCanRender(false);
|
||||
return;
|
||||
}
|
||||
if (typeof window.requestIdleCallback === "function") {
|
||||
idle = window.requestIdleCallback(() => setCanRender(true), {
|
||||
timeout: 2500,
|
||||
});
|
||||
} else {
|
||||
timer = setTimeout(() => setCanRender(true), 1200);
|
||||
}
|
||||
};
|
||||
updateCapability();
|
||||
preference.addEventListener("change", updateCapability);
|
||||
return () => preference.removeEventListener("change", updateCapability);
|
||||
return () => {
|
||||
cancelPending();
|
||||
preference.removeEventListener("change", updateCapability);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
Reference in new issue
Block a user