Skip to content

Commit 0486491

Browse files
committed
fix: update typing animation component
1 parent b6401f0 commit 0486491

File tree

4 files changed

+71
-28
lines changed

4 files changed

+71
-28
lines changed

content/docs/components/typing-animation.mdx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,11 @@ npx shadcn@latest add "https://magicui.design/r/typing-animation"
4242

4343
## Props
4444

45-
| Prop | Type | Description | Default |
46-
| --------- | ------ | --------------------------------------------- | ------- |
47-
| className | string | The class name to be applied to the component | |
48-
| duration | number | Duration to wait in between each char type. | 200 |
49-
| text | string | Text to animate | "" |
45+
| Prop | Type | Description | Default |
46+
| ----------- | ----------------- | --------------------------------------------- | ------- |
47+
| children | string | Text content to animate | |
48+
| className | string | The class name to be applied to the component | |
49+
| duration | number | Duration to wait in between each char type | 100 |
50+
| delay | number | Delay before animation starts (in ms) | 0 |
51+
| as | React.ElementType | Component to render as | "div" |
52+
| startOnView | boolean | Start animation when component is in view | false |
Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
import TypingAnimation from "@/registry/default/magicui/typing-animation";
22

33
export default function TypingAnimationDemo() {
4-
return (
5-
<TypingAnimation
6-
className="text-4xl font-bold text-black dark:text-white"
7-
text="Typing Animation"
8-
/>
9-
);
4+
return <TypingAnimation>Typing Animation</TypingAnimation>;
105
}
Lines changed: 61 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,70 @@
11
"use client";
22

3-
import { useEffect, useState } from "react";
4-
53
import { cn } from "@/lib/utils";
4+
import { motion, MotionProps } from "motion/react";
5+
import { useEffect, useRef, useState } from "react";
66

7-
interface TypingAnimationProps {
8-
text: string;
9-
duration?: number;
7+
interface TypingAnimationProps extends MotionProps {
8+
children: string;
109
className?: string;
10+
duration?: number;
11+
delay?: number;
12+
as?: React.ElementType;
13+
startOnView?: boolean;
1114
}
1215

1316
export default function TypingAnimation({
14-
text,
15-
duration = 200,
17+
children,
1618
className,
19+
duration = 100,
20+
delay = 0,
21+
as: Component = "div",
22+
startOnView = false,
23+
...props
1724
}: TypingAnimationProps) {
25+
const MotionComponent = motion.create(Component, {
26+
forwardMotionProps: true,
27+
});
28+
1829
const [displayedText, setDisplayedText] = useState<string>("");
19-
const [i, setI] = useState<number>(0);
30+
const [started, setStarted] = useState(false);
31+
const elementRef = useRef<HTMLElement | null>(null);
32+
33+
useEffect(() => {
34+
if (!startOnView) {
35+
const startTimeout = setTimeout(() => {
36+
setStarted(true);
37+
}, delay);
38+
return () => clearTimeout(startTimeout);
39+
}
40+
41+
const observer = new IntersectionObserver(
42+
([entry]) => {
43+
if (entry.isIntersecting) {
44+
setTimeout(() => {
45+
setStarted(true);
46+
}, delay);
47+
observer.disconnect();
48+
}
49+
},
50+
{ threshold: 0.1 }
51+
);
52+
53+
if (elementRef.current) {
54+
observer.observe(elementRef.current);
55+
}
56+
57+
return () => observer.disconnect();
58+
}, [delay, startOnView]);
2059

2160
useEffect(() => {
61+
if (!started) return;
62+
63+
let i = 0;
2264
const typingEffect = setInterval(() => {
23-
if (i < text.length) {
24-
setDisplayedText(text.substring(0, i + 1));
25-
setI(i + 1);
65+
if (i < children.length) {
66+
setDisplayedText(children.substring(0, i + 1));
67+
i++;
2668
} else {
2769
clearInterval(typingEffect);
2870
}
@@ -31,16 +73,18 @@ export default function TypingAnimation({
3173
return () => {
3274
clearInterval(typingEffect);
3375
};
34-
}, [duration, i]);
76+
}, [children, duration, started]);
3577

3678
return (
37-
<h1
79+
<MotionComponent
80+
ref={elementRef}
3881
className={cn(
39-
"font-display text-center text-4xl font-bold leading-[5rem] tracking-[-0.02em] drop-shadow-sm",
40-
className,
82+
"text-4xl font-bold leading-[5rem] tracking-[-0.02em]",
83+
className
4184
)}
85+
{...props}
4286
>
43-
{displayedText ? displayedText : text}
44-
</h1>
87+
{displayedText}
88+
</MotionComponent>
4589
);
4690
}

registry/registry-ui.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,7 @@ export const ui: Registry = [
396396
{
397397
name: "typing-animation",
398398
type: "registry:ui",
399+
dependencies: ["motion"],
399400
files: ["magicui/typing-animation.tsx"],
400401
},
401402
{

0 commit comments

Comments
 (0)