- Full-screen loader whose only content is the logo animating: the "B" draws in and fills, the base facets trace in, then a wave runs along them until the page is ready (pure CSS, plays before JS) - Loader gate holds the loader until GSAP + fonts and every 3D scene's first frame are ready, then waits 1s (min 1.4s total) before leaving; hard 8s limit, head-script fallback if JS never boots - Scroll reveals, SplitText and scene entry animations start only once the loader leaves, so above-the-fold entries play in view - Internal links fade the loader back in before navigating, so page changes are one seamless dark transition - No loader for no-JS or reduced-motion visitors Co-Authored-By: Claude Opus 5.5 <[email protected]>
32 lines
908 B
TypeScript
32 lines
908 B
TypeScript
import React from "react";
|
|
import { holdLoader } from "../loader/gate";
|
|
|
|
/**
|
|
* Boot the site-wide GSAP motion system (see ./motion.ts). Holds the page
|
|
* loader until GSAP is loaded and fonts are settled, so SplitText measures
|
|
* final line breaks.
|
|
*/
|
|
export default function useMotion() {
|
|
React.useEffect(() => {
|
|
let destroy: (() => void) | undefined;
|
|
let cancelled = false;
|
|
const release = holdLoader("motion");
|
|
|
|
Promise.all([
|
|
import("./motion"),
|
|
document.fonts?.ready.catch(() => {}),
|
|
])
|
|
.then(([{ initMotion }]) => {
|
|
if (!cancelled) destroy = initMotion();
|
|
})
|
|
.catch((error) => console.warn("Motion failed to load", error))
|
|
.finally(release);
|
|
|
|
return () => {
|
|
cancelled = true;
|
|
release();
|
|
destroy?.();
|
|
};
|
|
}, []);
|
|
}
|