Created
May 29, 2024 19:37
-
-
Save Muzammil-cyber/02df25a03d4f392c5c21e106f8ff785d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useState, useEffect, useRef } from "react"; | |
type AnimatedTextProps = { | |
text: string; | |
duration?: number; | |
className?: string; | |
}; | |
export default function AnimatedText({ | |
text, | |
duration = 10, | |
className, | |
}: AnimatedTextProps) { | |
const [currentChar, setCurrentChar] = useState(0); | |
const intervalRef = useRef(null); | |
useEffect(() => { | |
intervalRef.current = setInterval(() => { | |
if (currentChar === text.length) { | |
clearInterval(intervalRef.current); | |
return; | |
} | |
setCurrentChar((prev) => prev + 1); | |
}, duration * 1000 / text.length); // Adjust interval based on duration and text length | |
return () => clearInterval(intervalRef.current); // Cleanup function to stop interval on unmount | |
}, [text, duration]); | |
const displayedText = text.slice(0, currentChar); | |
return ( | |
<span className={className}> | |
{displayedText} | |
{!displayedText.endsWith(text) && <span className="animate-ping font-extralight text-inherit">|</span>} | |
</span> | |
); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use client"; | |
/** | |
* Importing necessary modules from their respective packages. | |
* | |
* `motion` is a component from the framer-motion library used to create animations. | |
* `useMotionValue` is a hook from the framer-motion library that creates a motion value. | |
* `useTransform` is a hook from the framer-motion library to create a new motion value by transforming another. | |
* `animate` is a function from the framer-motion library to animate a motion value. | |
*/ | |
import { motion, useMotionValue, useTransform, animate } from "framer-motion"; | |
/** | |
* Importing necessary hooks from React. | |
* | |
* `useEffect` is a hook that lets you perform side effects in your function components. | |
* `useState` is a hook that lets you add state to your functional components. | |
*/ | |
import { useEffect, useState } from "react"; | |
// Type definition for the props expected by the AnimatedText component. | |
type AnimatedTextProps = { | |
text: string; // The text to be animated. | |
duration?: number; // The duration of the animation in seconds. | |
className?: string; // The class name for the component. | |
}; | |
/** | |
* AnimatedText is a component that gradually reveals text from start to end, one character at a time. | |
* | |
* @param {string} text - The text to be animated. | |
*/ | |
export default function AnimatedText({ | |
text, | |
duration = 10, | |
className, | |
}: AnimatedTextProps) { | |
// `count` is a motion value that starts at 0 and will animate up to the length of the text. | |
const count = useMotionValue(0); | |
// `rounded` is a transformed motion value of `count`, rounding it to the nearest whole number. | |
const rounded = useTransform(count, (latest) => Math.round(latest)); | |
// `displayText` is a transformed motion value of `rounded`, slicing the text to the current count. | |
const displayText = useTransform(rounded, (latest) => text.slice(0, latest)); | |
// `animationCompleted` is a state variable to keep track of whether the animation has completed. | |
const [animationCompleted, setAnimationCompleted] = useState(false); | |
useEffect(() => { | |
/** | |
* Initiating the animation of the `count` motion value from 0 to the length of the text. | |
* The animation is linear over a 10 second duration. | |
* An `onUpdate` callback is specified to check if the animation is complete, and if so, `setAnimationCompleted` is called with `true`. | |
*/ | |
const controls = animate(count, text.length, { | |
type: "tween", | |
duration, | |
ease: "linear", | |
onUpdate: (latest) => { | |
if (latest === text.length) { | |
setAnimationCompleted(true); | |
} | |
}, | |
}); | |
// Returning a cleanup function to stop the animation when the component is unmounted. | |
return controls.stop; | |
// eslint-disable-next-line react-hooks/exhaustive-deps | |
}, []); // Empty dependency array means this useEffect will only run once, similar to `componentDidMount`. | |
return ( | |
/** | |
* Rendering a paragraph element with a class of `animation-completed` if the animation is complete, | |
* otherwise, it renders with an empty class string. | |
* Inside the paragraph, a `motion.span` element is rendered with the `displayText` motion value. | |
*/ | |
<span className={className}> | |
<motion.span className={className}>{displayText}</motion.span> | |
{!animationCompleted && ( | |
<motion.span className="animate-ping font-extralight text-inherit"> | |
| | |
</motion.span> | |
)} | |
</span> | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Gives a animation like it typing:
Example:
https://gist.github.com/assets/73660510/6fe1c048-ea59-4944-87a6-7341a703dfd1
Has to code one with framer one without framer.