subtitle flipper
This commit is contained in:
parent
a9c5059ad0
commit
0a14de0269
2 changed files with 72 additions and 4 deletions
|
|
@ -27,7 +27,12 @@ export const footerContent = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const heroContent = {
|
export const heroContent = {
|
||||||
subtitle: "Systementwickler",
|
subtitle: [
|
||||||
|
"Systementwickler",
|
||||||
|
"Creative Technologist",
|
||||||
|
"Low-Level Enthusiast",
|
||||||
|
"Graphics Programmer",
|
||||||
|
],
|
||||||
nameFirst: "MIKE",
|
nameFirst: "MIKE",
|
||||||
nameLast: "KARL",
|
nameLast: "KARL",
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ import { ScrollTrigger } from "gsap/ScrollTrigger";
|
||||||
import ParticleGrid from "../components/ParticleGrid";
|
import ParticleGrid from "../components/ParticleGrid";
|
||||||
import { heroContent, site } from "../data/site";
|
import { heroContent, site } from "../data/site";
|
||||||
|
|
||||||
|
const subtitles = heroContent.subtitle;
|
||||||
|
|
||||||
gsap.registerPlugin(ScrollTrigger);
|
gsap.registerPlugin(ScrollTrigger);
|
||||||
|
|
||||||
export default function HeroSection() {
|
export default function HeroSection() {
|
||||||
|
|
@ -80,9 +82,9 @@ export default function HeroSection() {
|
||||||
ref={contentRef}
|
ref={contentRef}
|
||||||
className="relative z-10 text-center px-6"
|
className="relative z-10 text-center px-6"
|
||||||
>
|
>
|
||||||
<p className="font-mono text-amber text-sm md:text-base tracking-[0.1em] uppercase mb-6">
|
<div className="font-mono text-amber text-sm md:text-base tracking-[0.1em] uppercase mb-6 overflow-hidden">
|
||||||
{heroContent.subtitle}
|
<SubtitleFlipper />
|
||||||
</p>
|
</div>
|
||||||
<h1
|
<h1
|
||||||
className="font-display font-bold text-text-primary leading-[0.9] tracking-tight"
|
className="font-display font-bold text-text-primary leading-[0.9] tracking-tight"
|
||||||
style={{ fontSize: "clamp(4rem, 12vw, 10rem)" }}
|
style={{ fontSize: "clamp(4rem, 12vw, 10rem)" }}
|
||||||
|
|
@ -101,3 +103,64 @@ export default function HeroSection() {
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function SubtitleFlipper() {
|
||||||
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const container = containerRef.current;
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
const items = container.querySelectorAll<HTMLSpanElement>(".subtitle-item");
|
||||||
|
if (items.length <= 1) return;
|
||||||
|
|
||||||
|
gsap.set(items, { yPercent: 100, opacity: 0 });
|
||||||
|
gsap.set(items[0], { yPercent: 0, opacity: 1 });
|
||||||
|
|
||||||
|
let current = 0;
|
||||||
|
let timeoutId: ReturnType<typeof setTimeout>;
|
||||||
|
const duration = 0.5;
|
||||||
|
const hold = 2500;
|
||||||
|
|
||||||
|
const showNext = () => {
|
||||||
|
const next = (current + 1) % items.length;
|
||||||
|
|
||||||
|
gsap.to(items[current], {
|
||||||
|
yPercent: -100,
|
||||||
|
opacity: 0,
|
||||||
|
duration,
|
||||||
|
ease: "power2.inOut",
|
||||||
|
});
|
||||||
|
|
||||||
|
gsap.fromTo(
|
||||||
|
items[next],
|
||||||
|
{ yPercent: 100, opacity: 0 },
|
||||||
|
{ yPercent: 0, opacity: 1, duration, ease: "power2.inOut" }
|
||||||
|
);
|
||||||
|
|
||||||
|
current = next;
|
||||||
|
timeoutId = setTimeout(showNext, hold);
|
||||||
|
};
|
||||||
|
|
||||||
|
timeoutId = setTimeout(showNext, hold);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
gsap.killTweensOf(items);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={containerRef} className="grid place-items-center">
|
||||||
|
{subtitles.map((text, i) => (
|
||||||
|
<span
|
||||||
|
key={text}
|
||||||
|
className="subtitle-item col-start-1 row-start-1 whitespace-nowrap"
|
||||||
|
aria-hidden={i !== 0}
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue