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

+19 -13
View File
@@ -111,26 +111,32 @@ export function ShowcaseProvider({
setActiveId(pickActiveCard(candidates, hovered.current));
}, []);
const schedule = useCallback(() => {
if (queued.current) return;
queued.current = true;
frame.current = requestAnimationFrame(evaluate);
}, [evaluate]);
// sem cards registrados (a home mostra só a vitrine animada) não há o que decidir a cada quadro de rolagem;
// `force` vale para o que acabou de mudar a lista (um card saiu), que ainda precisa ser reavaliado
const schedule = useCallback(
(force = false) => {
if (queued.current || (!force && cards.current.size === 0)) return;
queued.current = true;
frame.current = requestAnimationFrame(evaluate);
},
[evaluate],
);
// com a janela ampliada aberta, ou com preferência por menos movimento, nenhum card toca
useEffect(() => {
paused.current = reducedMotion || openId !== null || userPaused;
schedule();
schedule(true);
}, [schedule, openId, reducedMotion, userPaused]);
useEffect(() => {
window.addEventListener("scroll", schedule, { passive: true });
window.addEventListener("resize", schedule);
document.addEventListener("visibilitychange", schedule);
const onChange = () => schedule();
window.addEventListener("scroll", onChange, { passive: true });
window.addEventListener("resize", onChange);
document.addEventListener("visibilitychange", onChange);
return () => {
window.removeEventListener("scroll", schedule);
window.removeEventListener("resize", schedule);
document.removeEventListener("visibilitychange", schedule);
window.removeEventListener("scroll", onChange);
window.removeEventListener("resize", onChange);
document.removeEventListener("visibilitychange", onChange);
cancelAnimationFrame(frame.current);
queued.current = false;
observer.current?.disconnect();
@@ -163,7 +169,7 @@ export function ShowcaseProvider({
observer.current?.unobserve(element);
cards.current.delete(id);
if (hovered.current === id) hovered.current = null;
schedule();
schedule(true);
};
},
setHover(id, isHovered) {