52 lines
1.5 KiB
TypeScript
52 lines
1.5 KiB
TypeScript
"use client"
|
|
import { useRef, type HTMLAttributes, type ReactNode } from "react";
|
|
import { SplitText } from "gsap/SplitText";
|
|
import gsap from 'gsap'
|
|
import { cn } from "~/lib/utils";
|
|
import { useReveal } from "./useReveal";
|
|
|
|
const AnimateTextIn = ({
|
|
children,
|
|
animation = "type",
|
|
position = 0,
|
|
speed = 1,
|
|
scrollOnly = false,
|
|
once = false,
|
|
debugId,
|
|
className
|
|
}: {
|
|
children: ReactNode,
|
|
animation?: "type" | "slide",
|
|
position?: gsap.Position,
|
|
scrollOnly?: boolean,
|
|
once?: boolean,
|
|
debugId?: string,
|
|
speed?: number,
|
|
className?: HTMLAttributes<HTMLDivElement>['className']
|
|
}) => {
|
|
const el = useRef<HTMLDivElement>(null)
|
|
useReveal(el, {
|
|
position,
|
|
scrollOnly,
|
|
once,
|
|
debugId: debugId ?? `text-${position}`,
|
|
makeReveal: (node) => {
|
|
// The wrapper starts at opacity 0 (so there's no flash of unsplit text);
|
|
// reveal the wrapper and let the per-character tween do the animation.
|
|
gsap.set(node, { opacity: 1 })
|
|
const split = new SplitText(node, { type: 'chars' })
|
|
const fromVars = animation === "slide"
|
|
? { opacity: 0, x: -10, duration: 0.2 * speed, stagger: { each: 0.08 * speed }, ease: 'bounce.inOut' }
|
|
: { opacity: 0, duration: 0.01 * speed, stagger: { each: 0.04 * speed }, ease: 'bounce.inOut' }
|
|
return gsap.from(split.chars, { ...fromVars, paused: true })
|
|
},
|
|
})
|
|
return (
|
|
<div ref={el} className={cn(className, "opacity-0")}>
|
|
{children}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default AnimateTextIn;
|