audiocnSolid

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-visibility

Usage

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();
  }
});