useVisibility
Whether an element is on screen, in a ref, so animation loops can skip frames nobody sees.
Every audiocn meter and visualizer that paints on animation frames uses this hook to stop painting while it is scrolled out of view.
$ pnpm dlx shadcn@latest add @audiocn-solid/use-visibilityUsage
import { createEffect, onCleanup } from "solid-js";
import { useVisibility } from "@/hooks/use-visibility";
export const Spinner = () => {
let ref!: HTMLDivElement;
const visibleRef = useVisibility(() => ref);
createEffect(() => {
let frame = requestAnimationFrame(function tick(now) {
if (visibleRef.current) {
ref.style.setProperty("--angle", `${(now / 10) % 360}deg`);
}
frame = requestAnimationFrame(tick);
});
onCleanup(() => cancelAnimationFrame(frame));
});
return <div class="size-4 rotate-(--angle) bg-primary" ref={ref} />;
};The hook returns a ref, not state: reading visibleRef.current inside a loop costs nothing and never re-renders. It reads true until the first observation, and stays true where IntersectionObserver is unavailable.
Waking a sleeping loop
Pass onChange to hear when the element comes into view or leaves it. audiocn painters use it with createFrameTask from lib/audio/frame-loop: a meter that is off screen, or has settled, requests no frames at all, and coming back into view wakes it.
let task: FrameTask | null = null;
const visibleRef = useVisibility(ref, (visible) => {
if (visible) {
task?.wake();
}
});