xlibs-components
Version:
Modern React component library with Aceternity, MagicUI, and ShadCN components for building beautiful web applications
52 lines (51 loc) • 1.92 kB
JavaScript
"use client";
import { jsx as _jsx } from "react/jsx-runtime";
import { cn } from '../../lib/utils.js';
import { motion } from "motion/react";
import { useEffect, useRef, useState } from "react";
export function TypingAnimation({ children, className, duration = 100, delay = 0, as: Component = "div", startOnView = false, ...props }) {
const MotionComponent = motion.create(Component, {
forwardMotionProps: true,
});
const [displayedText, setDisplayedText] = useState("");
const [started, setStarted] = useState(false);
const elementRef = useRef(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 < children.length) {
setDisplayedText(children.substring(0, i + 1));
i++;
}
else {
clearInterval(typingEffect);
}
}, duration);
return () => {
clearInterval(typingEffect);
};
}, [children, duration, started]);
return (_jsx(MotionComponent, { ref: elementRef, className: cn("text-4xl font-bold leading-[5rem] tracking-[-0.02em]", className), ...props, children: displayedText }));
}