agency-web/app/components/SectionDivider.tsx

74 lines
2.4 KiB
TypeScript

"use client";
/**
* Cinematic section divider — a thin gradient rule that DRAWS itself across the
* viewport (GSAP DrawSVG) as it scrolls into view, with a travelling node that
* rides along it. Pure decoration; adds rhythm + "life" between sections so the
* page never reads as empty stacked blocks.
* Reduced-motion: renders fully drawn, no travel.
*/
import { useEffect, useRef } from "react";
import { gsap } from "./gsap";
export default function SectionDivider({ label }: { label?: string }) {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const el = ref.current;
if (!el) return;
const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (reduce) return;
const ctx = gsap.context(() => {
const tl = gsap.timeline({
scrollTrigger: { trigger: el, start: "top 88%", once: true },
});
tl.from(".divider__line", {
drawSVG: "50% 50%",
duration: 1.2,
ease: "power2.inOut",
})
.from(
".divider__node",
{ scale: 0, transformOrigin: "center", duration: 0.5, ease: "back.out(2.2)" },
"-=0.4"
)
.from(
".divider__label",
{ autoAlpha: 0, y: 8, filter: "blur(4px)", duration: 0.6, ease: "power2.out" },
"-=0.4"
);
// node drifts gently along the line on scroll => parallax life
gsap.to(".divider__node", {
x: 120,
ease: "none",
scrollTrigger: { trigger: el, start: "top bottom", end: "bottom top", scrub: 1 },
});
}, el);
return () => ctx.revert();
}, []);
return (
<div ref={ref} className="divider wrap" aria-hidden="true">
{label && <span className="divider__label">{label}</span>}
<svg className="divider__svg" viewBox="0 0 1000 12" preserveAspectRatio="none">
<defs>
<linearGradient id="dividerGrad" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stopColor="#3b82f6" stopOpacity="0.1" />
<stop offset="50%" stopColor="#8b5cf6" stopOpacity="0.9" />
<stop offset="100%" stopColor="#10b981" stopOpacity="0.1" />
</linearGradient>
</defs>
<line
className="divider__line"
x1="0" y1="6" x2="1000" y2="6"
stroke="url(#dividerGrad)"
strokeWidth="1.5"
/>
</svg>
<span className="divider__node" />
</div>
);
}