57 lines
2.0 KiB
TypeScript
57 lines
2.0 KiB
TypeScript
/** Gestos que mostram alguém olhando para a página: qualquer um deles religa a batida. */
|
|
const ACTIVITY = [
|
|
"pointermove",
|
|
"pointerdown",
|
|
"scroll",
|
|
"wheel",
|
|
"touchmove",
|
|
"keydown",
|
|
] as const;
|
|
|
|
export type FramePulseOptions = {
|
|
/** Quadros por segundo pedidos (padrão 30). */
|
|
fps?: number;
|
|
/** Tempo (ms) sem cursor, rolagem nem teclado depois do qual a batida para de vez (padrão 5000). */
|
|
idleMs?: number;
|
|
};
|
|
|
|
/**
|
|
* Batida das cenas WebGL em demanda: pede um quadro (`invalidate`) a no máximo `fps` por segundo, de um único
|
|
* `setInterval` (fundo ambiente, sem gastar 60 quadros por segundo e sem um segundo laço de requestAnimationFrame ao
|
|
* lado do da cena). Depois de `idleMs` sem cursor, rolagem nem teclado, a batida para de vez (nenhum intervalo, nenhum
|
|
* quadro) e o último quadro fica na tela; qualquer movimento a religa. É o único relógio do cursor (nenhum
|
|
* requestAnimationFrame encadeado a cada quadro da tela); quem precisa de mais (entrada, rolagem) pede o próximo quadro
|
|
* por conta própria, dentro do quadro. Devolve a função que desliga tudo.
|
|
*/
|
|
export function startFramePulse(
|
|
invalidate: () => void,
|
|
{ fps = 30, idleMs = 5000 }: FramePulseOptions = {},
|
|
) {
|
|
const period = Math.round(1000 / fps);
|
|
let timer: ReturnType<typeof setInterval> | undefined;
|
|
let lastActivity = performance.now();
|
|
|
|
const stopTimer = () => {
|
|
clearInterval(timer);
|
|
timer = undefined;
|
|
};
|
|
const beat = () => {
|
|
// parada por inatividade: sem intervalo até o próximo movimento
|
|
if (performance.now() - lastActivity > idleMs) stopTimer();
|
|
else invalidate();
|
|
};
|
|
const wake = () => {
|
|
lastActivity = performance.now();
|
|
if (timer === undefined) timer = setInterval(beat, period);
|
|
};
|
|
|
|
for (const type of ACTIVITY)
|
|
window.addEventListener(type, wake, { passive: true });
|
|
wake();
|
|
invalidate();
|
|
return () => {
|
|
for (const type of ACTIVITY) window.removeEventListener(type, wake);
|
|
stopTimer();
|
|
};
|
|
}
|