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:
1 parent
8d07b8c6bb
commit
c9a0b80656
48 files changed
+7210
-1241
No files matched your search
@@ -0,0 +1,36 @@
|
||||
"use client";
|
||||
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import { useEffect } from "react";
|
||||
|
||||
/**
|
||||
* Batida das cenas WebGL em `frameloop="demand"`: enquanto `active`, pede um quadro a no máximo `fps` por
|
||||
* segundo (fundo ambiente, sem gastar 60 quadros por segundo); com `active` falso, nenhum quadro é desenhado.
|
||||
* Quem precisa de mais (entrada, rolagem, cursor) pede o próximo quadro com `invalidate()` dentro do useFrame.
|
||||
*/
|
||||
export default function FramePulse({
|
||||
active,
|
||||
fps = 30,
|
||||
}: {
|
||||
active: boolean;
|
||||
fps?: number;
|
||||
}) {
|
||||
const invalidate = useThree((state) => state.invalidate);
|
||||
|
||||
useEffect(() => {
|
||||
if (!active) return;
|
||||
const gap = 1000 / fps - 2;
|
||||
let id = 0;
|
||||
let last = 0;
|
||||
const beat = (now: number) => {
|
||||
id = requestAnimationFrame(beat);
|
||||
if (now - last < gap) return;
|
||||
last = now;
|
||||
invalidate();
|
||||
};
|
||||
id = requestAnimationFrame(beat);
|
||||
return () => cancelAnimationFrame(id);
|
||||
}, [active, fps, invalidate]);
|
||||
|
||||
return null;
|
||||
}
|
||||
Reference in new issue
Block a user