perf(home): transicoes de Qualidade so com translate/opacity e titulos por palavra em CSS

Medido com trace do Chromium (rasterizacao no mesmo trecho de rolagem):
IA->Qualidade 840->558 ms, dentro de Qualidade 34->18 ms, Qualidade->Sobre
819->643 ms. Recorte animado, escala de camadas grandes e o desenho do diagrama
a cada quadro saem; o diagrama se desenha uma vez em CSS. TextDiffuseReveal
deixa de animar cada letra com desfoque.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Evandro Fonseca JuniorandClaude Opus 5.5 committed 2026-10-05 01:19:48 -03:00
1 parent 44efcfcb20
commit def1e22302
5 files changed
+121 -215

No files matched your search

+13 -32
View File
@@ -85,48 +85,27 @@ const TRANSITIONS: Transition[] = [
0,
);
},
// IA → qualidade: a tela se abre do centro como duas portas, e o diagrama se desenha.
// Só 2D: com rotação 3D o Chrome adiava a pintura do diagrama (camada grande demais).
// IA → qualidade: a seção sobe mais rápido que a rolagem, como um cartão, sobre a anterior que escurece.
// Só translate/opacity: recorte animado, escala (o Chrome rasteriza de novo a cada novo tamanho) e o
// desenho do diagrama a cada quadro dobravam o custo de pintura desta passagem. O diagrama se desenha
// uma vez, em CSS, quando a passagem começa (classe is-drawing, ver onEnter).
(tl, prev, next) => {
holdPrev(tl, prev);
holdNext(tl, next);
tl.fromTo(
prev,
{ scale: 1, opacity: 1, transformOrigin: visibleCenter(prev) },
{ scale: 0.9, opacity: 0.25 },
0,
).fromTo(
tl.fromTo(prev, { opacity: 1 }, { opacity: 0.2 }, 0).fromTo(
next,
{ clipPath: () => `inset(0px 50% ${next.offsetHeight - vh()}px 50%)` },
{ clipPath: () => `inset(0px 0% ${next.offsetHeight - vh()}px 0%)`, ease: "power2.inOut" },
{ yPercent: 0, y: () => vh() * 0.35 },
{ y: 0, ease: "power2.out" },
0,
);
const lines = next.querySelectorAll(
".quality-dimensions path, .quality-browser-frame, .quality-blueprint > path.quality-wire, .quality-blueprint > rect.quality-wire, .quality-blueprint > g.quality-wire > *, .quality-browser-button",
);
const labels = next.querySelectorAll(
".quality-blueprint text, .quality-browser-dots, .quality-visual-label, .quality-visual figcaption",
);
if (lines.length) {
tl.set(lines, { attr: { pathLength: 1 }, strokeDasharray: 1 }, 0).fromTo(
lines,
{ strokeDashoffset: 1 },
{ strokeDashoffset: 0, duration: 0.4, stagger: { amount: 0.25 } },
0.3,
);
}
if (labels.length) {
tl.fromTo(labels, { opacity: 0 }, { opacity: 1, duration: 0.25, stagger: { amount: 0.2 } }, 0.5);
}
},
// qualidade → sobre: cortina diagonal
// qualidade → sobre: a página entra girando pela esquerda, como uma folha virando (só transform)
(tl, prev, next) => {
holdPrev(tl, prev);
holdNext(tl, next);
tl.fromTo(prev, { opacity: 1, xPercent: 0 }, { opacity: 0.25, xPercent: 8 }, 0).fromTo(
tl.fromTo(prev, { opacity: 1, xPercent: 0 }, { opacity: 0.25, xPercent: 20 }, 0).fromTo(
next,
{ clipPath: () => `polygon(0% 0px, 0% 0px, -30% ${vh()}px, -30% ${vh()}px)` },
{ clipPath: () => `polygon(0% 0px, 130% 0px, 100% ${vh()}px, -30% ${vh()}px)` },
{ xPercent: -100, rotation: -7, transformOrigin: "0% 0%" },
{ xPercent: 0, rotation: 0, ease: "power2.out" },
0,
);
},
@@ -199,6 +178,8 @@ export function useCinematicScroll(mainRef: RefObject<HTMLElement | null>) {
invalidateOnRefresh: true,
// passagem concluída: o recorte já cobre a tela inteira; sem ele a seção pinta normalmente
onLeave: () => gsap.set(next, { clipPath: "none" }),
// diagrama de qualidade: desenha uma vez quando a seção começa a entrar
onEnter: () => next.classList.add("is-drawing"),
},
});
transition(tl, prev, next);