67 lines
1.4 KiB
TypeScript
67 lines
1.4 KiB
TypeScript
"use client";
|
|
|
|
import React from "react";
|
|
import { motion } from "motion/react";
|
|
import type { Variants } from "motion/react";
|
|
import { TRANSITIONS } from "@/lib/animation";
|
|
|
|
type SplitTextRevealProps = {
|
|
text: string;
|
|
className?: string;
|
|
once?: boolean;
|
|
wordClassName?: string;
|
|
stagger?: number;
|
|
};
|
|
|
|
export function SplitTextReveal({
|
|
text,
|
|
className,
|
|
once = true,
|
|
wordClassName,
|
|
stagger = 0.06,
|
|
}: SplitTextRevealProps) {
|
|
const words = text.trim().split(/\s+/);
|
|
|
|
const container: Variants = {
|
|
initial: {},
|
|
animate: {
|
|
transition: {
|
|
staggerChildren: stagger,
|
|
},
|
|
},
|
|
};
|
|
|
|
const child: Variants = {
|
|
initial: { opacity: 0, y: 8, filter: "blur(6px)" },
|
|
animate: {
|
|
opacity: 1,
|
|
y: 0,
|
|
filter: "blur(0px)",
|
|
transition: TRANSITIONS.base,
|
|
},
|
|
};
|
|
|
|
return (
|
|
<motion.span
|
|
className={className}
|
|
style={{ display: "inline-block" }}
|
|
variants={container}
|
|
initial="initial"
|
|
whileInView="animate"
|
|
viewport={{ once, margin: "0px 0px -10% 0px" }}
|
|
>
|
|
{words.map((w, i) => (
|
|
<motion.span
|
|
key={`${w}-${i}`}
|
|
className={wordClassName}
|
|
style={{ display: "inline-block", willChange: "transform" }}
|
|
variants={child}
|
|
>
|
|
{w}
|
|
{i < words.length - 1 ? " " : ""}
|
|
</motion.span>
|
|
))}
|
|
</motion.span>
|
|
);
|
|
}
|