perf(movimento): uma transição, 3D leve e vídeo certo por aparelho

Passagens com fade e 32 px, cena anterior termina em opacidade 0, sem visibility nem clip-path. 3D do hero como fundo ambiente, sob demanda e desligado em toque, aparelho fraco, saveData e movimento reduzido. Lenis fora do toque e salto direto em âncoras distantes. Vitrine só de clientes, até 3 capítulos, fixa só no desktop e carrossel no celular; vídeo só do projeto em cena com preload metadata. Rótulos mono a partir de 12 px e limão só no contato.
This commit is contained in:
Evandro Fonseca Junior committed 2026-10-14 03:32:23 -03:00
1 parent 8d07b8c6bb
commit c9a0b80656
48 files changed
+7210 -1241

No files matched your search

+21 -6
View File
@@ -6,10 +6,13 @@ import { getProjectVideo, getShowcaseConfig } from "@/lib/project-showcase";
import { ProjectVideo } from "../ProjectVideo";
import { BrowserFrame, displayUrl } from "./BrowserFrame";
import { useShowcaseCard } from "./ShowcaseProvider";
import { useNearViewport, useViewportDevice } from "./useShowcaseMedia";
/**
* Capa do card com vídeo: o vídeo dentro de uma moldura de navegador, com brilho da cor do site quando é o
* card que toca, e o botão que amplia (a tela inteira é o botão).
* card que toca, e o botão que amplia (a tela inteira é o botão). A versão do aparelho (celular abaixo de
* 768 px) só é escolhida no navegador; no servidor e antes de a capa chegar a ~1 tela da janela, nem o pôster
* é pedido (a moldura já reserva o espaço).
*/
export function ProjectMedia({
project,
@@ -18,15 +21,16 @@ export function ProjectMedia({
id: string;
title: string;
demoUrl?: string | null;
githubUrl?: string | null;
};
}) {
const { frameRef, active, open, userPaused, toggleAutoplay } =
useShowcaseCard(project.id);
const reducedMotion = useReducedMotion();
const video = getProjectVideo(project.id);
const device = useViewportDevice();
const near = useNearViewport(frameRef);
const video = device ? getProjectVideo(project.id, device) : undefined;
const config = getShowcaseConfig(project.id);
if (!video || !config) return null;
if (!getProjectVideo(project.id) || !config) return null;
return (
<div
@@ -38,9 +42,20 @@ export function ProjectMedia({
<BrowserFrame url={displayUrl(project)}>
<div
className="ds-frame-screen"
style={{ backgroundImage: `url(${video.poster})` }}
style={
video && near
? { backgroundImage: `url(${video.poster})` }
: undefined
}
>
<ProjectVideo video={video} title={project.title} active={active} />
{video && (
<ProjectVideo
video={video}
title={project.title}
active={active}
showPoster={near}
/>
)}
<button
type="button"
className="ds-frame-expand"