Shimmer wave effect on text.
1'use client';
2import { type JSX } from 'react';
3import { motion, Transition } from 'motion/react';
4import { cn } from '@/lib/utils';
5
6export type TextShimmerWaveProps = {
7 children: string;
8 as?: React.ElementType;
9 className?: string;
10 duration?: number;
11 zDistance?: number;
12 xDistance?: number;
13 yDistance?: number;
14 spread?: number;
15 scaleDistance?: number;
16 rotateYDistance?: number;
17 transition?: Transition;
18};
19
20export function TextShimmerWave({
21 children,
22 as: Component = 'p',
23 className,
24 duration = 1,
25 zDistance = 10,
26 xDistance = 2,
27 yDistance = -2,
28 spread = 1,
29 scaleDistance = 1.1,
30 rotateYDistance = 10,
31 transition,
32}: TextShimmerWaveProps) {
33 const MotionComponent = motion.create(
34 Component as keyof JSX.IntrinsicElements
35 );
36
37 return (
38 <MotionComponent
39 className={cn(
40 'relative inline-block [perspective:500px]',
41 '[--base-color:#a1a1aa] [--base-gradient-color:#000]',
42 'dark:[--base-color:#71717a] dark:[--base-gradient-color:#ffffff]',
43 className
44 )}
45 style={{ color: 'var(--base-color)' }}
46 >
47 {children.split('').map((char, i) => {
48 const delay = (i * duration * (1 / spread)) / children.length;
49
50 return (
51 <motion.span
52 key={i}
53 className={cn(
54 'inline-block whitespace-pre [transform-style:preserve-3d]'
55 )}
56 initial={{
57 translateZ: 0,
58 scale: 1,
59 rotateY: 0,
60 color: 'var(--base-color)',
61 }}
62 animate={{
63 translateZ: [0, zDistance, 0],
64 translateX: [0, xDistance, 0],
65 translateY: [0, yDistance, 0],
66 scale: [1, scaleDistance, 1],
67 rotateY: [0, rotateYDistance, 0],
68 color: [
69 'var(--base-color)',
70 'var(--base-gradient-color)',
71 'var(--base-color)',
72 ],
73 }}
74 transition={{
75 duration: duration,
76 repeat: Infinity,
77 repeatDelay: (children.length * 0.05) / spread,
78 delay,
79 ease: 'easeInOut',
80 ...transition,
81 }}
82 >
83 {char}
84 </motion.span>
85 );
86 })}
87 </MotionComponent>
88 );
89}
90