- Wrap getDictionary with React cache to prevent redundant JSON parsing on the server - Create MotionProvider to globally initialize framer-motion LazyMotion with domAnimation - Replace standard 'motion' imports with 'm' across all animated client components (HomeClient, Navbar, GaleriClient, SSSClient, card, text-reveal) to drastically reduce First Load JS size
50 lines
1.1 KiB
TypeScript
50 lines
1.1 KiB
TypeScript
"use client";
|
|
|
|
import { m, useInView } from "framer-motion";
|
|
import { useRef } from "react";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
interface TextRevealProps {
|
|
text: string;
|
|
className?: string;
|
|
delay?: number;
|
|
once?: boolean;
|
|
stagger?: number;
|
|
}
|
|
|
|
export function TextReveal({
|
|
text,
|
|
className,
|
|
delay = 0,
|
|
once = true,
|
|
stagger = 0.055,
|
|
}: TextRevealProps) {
|
|
const ref = useRef<HTMLSpanElement>(null);
|
|
const isInView = useInView(ref, { once, margin: "-60px" });
|
|
const words = text.split(" ");
|
|
|
|
return (
|
|
<span ref={ref} className={cn("inline", className)}>
|
|
{words.map((word, i) => (
|
|
<m.span
|
|
key={i}
|
|
className="inline-block mr-[0.28em] last:mr-0"
|
|
initial={{ opacity: 0, y: 22, filter: "blur(4px)" }}
|
|
animate={
|
|
isInView
|
|
? { opacity: 1, y: 0, filter: "blur(0px)" }
|
|
: { opacity: 0, y: 22, filter: "blur(4px)" }
|
|
}
|
|
transition={{
|
|
duration: 0.55,
|
|
delay: delay + i * stagger,
|
|
ease: [0.16, 1, 0.3, 1],
|
|
}}
|
|
>
|
|
{word}
|
|
</m.span>
|
|
))}
|
|
</span>
|
|
);
|
|
}
|