Skip to content

Instantly share code, notes, and snippets.

@Muzammil-cyber
Created May 29, 2024 19:37
Show Gist options
  • Save Muzammil-cyber/02df25a03d4f392c5c21e106f8ff785d to your computer and use it in GitHub Desktop.
Save Muzammil-cyber/02df25a03d4f392c5c21e106f8ff785d to your computer and use it in GitHub Desktop.
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>
);
}
"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>
);
}
@Muzammil-cyber
Copy link
Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment