Skip to content

Commit

Permalink
fix: update typing animation component (magicuidesign#449)
Browse files Browse the repository at this point in the history
* fix: update typing animation component

* fix: lint

* fix: add motion
  • Loading branch information
dillionverma authored Dec 18, 2024
1 parent 5038335 commit fd9224d
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 31 deletions.
13 changes: 8 additions & 5 deletions content/docs/components/typing-animation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,11 @@ npx shadcn@latest add "https://magicui.design/r/typing-animation"

## Props

| Prop | Type | Description | Default |
| --------- | ------ | --------------------------------------------- | ------- |
| className | string | The class name to be applied to the component | |
| duration | number | Duration to wait in between each char type. | 200 |
| text | string | Text to animate | "" |
| Prop | Type | Description | Default |
| ----------- | ----------------- | --------------------------------------------- | ------- |
| children | string | Text content to animate | |
| className | string | The class name to be applied to the component | |
| duration | number | Duration to wait in between each char type | 100 |
| delay | number | Delay before animation starts (in ms) | 0 |
| as | React.ElementType | Component to render as | "div" |
| startOnView | boolean | Start animation when component is in view | false |
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,14 @@
"@radix-ui/react-toast": "^1.2.1",
"@radix-ui/react-tooltip": "^1.1.2",
"@t3-oss/env-nextjs": "^0.7.1",
"concurrently": "^9.0.1",
"@types/lodash.template": "^4.5.3",
"autoprefixer": "10.4.14",
"canvas-confetti": "^1.9.3",
"class-variance-authority": "^0.6.0",
"clsx": "^2.1.1",
"cmdk": "^1.0.0",
"cobe": "^0.6.3",
"concurrently": "^9.0.1",
"crisp-sdk-web": "^1.0.25",
"critters": "^0.0.24",
"date-fns": "^2.30.0",
Expand All @@ -60,6 +60,7 @@
"jotai": "^2.10.0",
"lodash.template": "^4.5.0",
"lucide-react": "^0.401.0",
"motion": "^11.15.0",
"next": "14.2.13",
"next-themes": "^0.3.0",
"octokit": "^4.0.2",
Expand Down
66 changes: 63 additions & 3 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 1 addition & 6 deletions registry/default/example/typing-animation-demo.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import TypingAnimation from "@/registry/default/magicui/typing-animation";

export default function TypingAnimationDemo() {
return (
<TypingAnimation
className="text-4xl font-bold text-black dark:text-white"
text="Typing Animation"
/>
);
return <TypingAnimation>Typing Animation</TypingAnimation>;
}
76 changes: 60 additions & 16 deletions registry/default/magicui/typing-animation.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,70 @@
"use client";

import { useEffect, useState } from "react";

import { cn } from "@/lib/utils";
import { motion, MotionProps } from "motion/react";
import { useEffect, useRef, useState } from "react";

interface TypingAnimationProps {
text: string;
duration?: number;
interface TypingAnimationProps extends MotionProps {
children: string;
className?: string;
duration?: number;
delay?: number;
as?: React.ElementType;
startOnView?: boolean;
}

export default function TypingAnimation({
text,
duration = 200,
children,
className,
duration = 100,
delay = 0,
as: Component = "div",
startOnView = false,
...props
}: TypingAnimationProps) {
const MotionComponent = motion.create(Component, {
forwardMotionProps: true,
});

const [displayedText, setDisplayedText] = useState<string>("");
const [i, setI] = useState<number>(0);
const [started, setStarted] = useState(false);
const elementRef = useRef<HTMLElement | null>(null);

useEffect(() => {
if (!startOnView) {
const startTimeout = setTimeout(() => {
setStarted(true);
}, delay);
return () => clearTimeout(startTimeout);
}

const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setTimeout(() => {
setStarted(true);
}, delay);
observer.disconnect();
}
},
{ threshold: 0.1 },
);

if (elementRef.current) {
observer.observe(elementRef.current);
}

return () => observer.disconnect();
}, [delay, startOnView]);

useEffect(() => {
if (!started) return;

let i = 0;
const typingEffect = setInterval(() => {
if (i < text.length) {
setDisplayedText(text.substring(0, i + 1));
setI(i + 1);
if (i < children.length) {
setDisplayedText(children.substring(0, i + 1));
i++;
} else {
clearInterval(typingEffect);
}
Expand All @@ -31,16 +73,18 @@ export default function TypingAnimation({
return () => {
clearInterval(typingEffect);
};
}, [duration, i]);
}, [children, duration, started]);

return (
<h1
<MotionComponent
ref={elementRef}
className={cn(
"font-display text-center text-4xl font-bold leading-[5rem] tracking-[-0.02em] drop-shadow-sm",
"text-4xl font-bold leading-[5rem] tracking-[-0.02em]",
className,
)}
{...props}
>
{displayedText ? displayedText : text}
</h1>
{displayedText}
</MotionComponent>
);
}
1 change: 1 addition & 0 deletions registry/registry-ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,7 @@ export const ui: Registry = [
{
name: "typing-animation",
type: "registry:ui",
dependencies: ["motion"],
files: ["magicui/typing-animation.tsx"],
},
{
Expand Down

0 comments on commit fd9224d

Please sign in to comment.