Files
Evandro Fonseca Junior 318a764190 perf(movimento): sem tela vazia, passagens em CSS e vitrine leve
Projetos sobem sobre o palco do hero e o celular tem hero de uma tela sem mergulho; passagens com animation-timeline em CSS, sem GSAP nem motion; troca da vitrine sem texto duplicado; moldura de celular só com pôster e scrub reencodado em 960 px (9,4 para 5,2 MB); 3D monta na primeira interação e para quando parado; transição entre páginas do mesmo segmento; foco em anel duplo; texto de 15 px no celular; código morto removido.
2026-10-14 14:15:50 -03:00

55 lines
1.6 KiB
TypeScript

"use client";
import { type RefObject, useEffect, useState } from "react";
interface InViewOptions {
/** Fração do elemento (0 a 1) que precisa estar à vista; em elementos altos, vale também essa fração da altura da janela. */
amount?: number;
/** Verdadeiro (padrão): depois de aparecer uma vez, continua verdadeiro. Falso: acompanha a entrada e a saída. */
once?: boolean;
}
function isShown(entry: IntersectionObserverEntry, amount: number) {
return (
entry.isIntersecting &&
(entry.intersectionRatio >= amount ||
entry.intersectionRect.height >= window.innerHeight * amount)
);
}
/**
* O elemento está à vista? Usa IntersectionObserver (sem ouvir a rolagem). Sem IntersectionObserver devolve
* verdadeiro logo na montagem: o conteúdo nunca fica escondido por falta do recurso.
*/
export function useInView(
ref: RefObject<Element | null>,
{ amount = 0.2, once = true }: InViewOptions = {},
): boolean {
const [inView, setInView] = useState(false);
useEffect(() => {
const element = ref.current;
if (!element) return;
if (typeof IntersectionObserver === "undefined") {
setInView(true);
return;
}
const observer = new IntersectionObserver(
(entries) => {
for (const entry of entries) {
const shown = isShown(entry, amount);
if (shown) {
setInView(true);
if (once) observer.disconnect();
} else if (!once) setInView(false);
}
},
{ threshold: [0, amount] },
);
observer.observe(element);
return () => observer.disconnect();
}, [ref, amount, once]);
return inView;
}