Files
portifolio-evandrodevbr/app/components/showcase/ProjectReel.tsx
T
Evandro Fonseca Junior 6ac87824f3 perf(movimento): sem tempo morto, vídeos regravados e 3D sem React Three Fiber
Legenda do mergulho até Projetos cobrir a cena; transição de página sem blur e com fundo escuro; foco no conteúdo ao trocar de página; listas com marcador e dois pesos de título; vídeos da SEV e da SC Plus regravados sem quadros brancos; scrub a 12-15 fps (-30%); vitrine com uma parada de Tab e setas, contador no celular e CSS sob demanda; âncoras num módulo só (fragment-nav); FramePulse com setInterval; CSS morto removido.
2026-10-14 17:01:37 -03:00

64 lines
2.3 KiB
TypeScript

"use client";
import { type CSSProperties, Suspense } from "react";
import { useReducedMotion } from "@/app/hooks/useReducedMotion";
import { PinnedReelLazy } from "./PinnedReelLazy";
import { ProjectList } from "./ProjectList";
import { type ShowcaseItem, useShowcaseItems } from "./ShowcaseProvider";
import { usePinnedViewport, useViewportDevice } from "./useShowcaseMedia";
import "./reel-list.css";
/**
* O bloco que segura o lugar da vitrine fixa, com a mesma altura dela: a página não muda de tamanho quando ela
* aparece (o salto para #contact, por exemplo, cai no lugar certo). O CSS só o mostra onde a vitrine fixa vai existir.
*/
function ReelPlaceholder({ items }: { items: ShowcaseItem[] }) {
return (
<div
className="ds-reel ds-reel-placeholder"
aria-hidden="true"
style={
{
"--reel-sum": items.reduce(
(sum, item) => sum + item.config.weight,
0,
),
} as CSSProperties
}
/>
);
}
/**
* Projetos da home. Em tablet e desktop (768 px de largura e 561 px de altura, no mínimo), a vitrine animada:
* o site de cada projeto rola dentro de um notebook (e de um celular ao lado) conforme a página rola, com a
* cena presa (position: sticky) durante a rolagem de todos os projetos. No celular, com prefers-reduced-motion
* e em janelas baixas, nada fica preso: é a lista de projetos (carrossel por toque no celular), só com pôsteres.
* O servidor entrega a lista (texto e links); o navegador troca pela vitrine fixa quando ela cabe, e até lá
* (e enquanto o pacote dela chega) um bloco da mesma altura segura o lugar. Funciona sozinha dentro de
* `ShowcaseProvider`: não depende dos cards da grade.
*/
export function ProjectReel() {
const items = useShowcaseItems();
const reducedMotion = useReducedMotion();
const wide = usePinnedViewport();
const device = useViewportDevice();
if (items.length === 0) return null;
const mounted = wide !== null && device !== null;
if (mounted && wide && !reducedMotion)
return (
<Suspense fallback={<ReelPlaceholder items={items} />}>
<PinnedReelLazy items={items} />
</Suspense>
);
return (
<>
<ProjectList items={items} device={device} pending={!mounted} />
{!mounted && <ReelPlaceholder items={items} />}
</>
);
}