Decrypting Text
Displays text with a decrypting animation effect, revealing the final content through randomized characters.
decrypting-text-animation-preview.jsx
import DecryptingTextAnimation from "@/components/text-effects/decrypting-text-animation/decrypting-text-animation";
/* keep text on center */
const wrapperStyles = {
textAlign: "center",
padding: "24px",
textWrap: "balance",
};
const DecryptingTextAnimationPreview = () => {
return (
<div style={wrapperStyles}>
<p style={{
fontFamily: "monospace",
fontSize: "1.5rem",
}}>
Beyond the encrypted noise lives {" "}
<strong>
<DecryptingTextAnimation
text="Pure Awareness"
speed={50}
/>
</strong>
</p>
</div>
);
};
export default DecryptingTextAnimationPreview; Installation
Run the command from your project root directory (the folder that contains package.json).
Terminal / Console
npx mosaicui-cli@latest text-effects/decrypting-text-animation 1. Copy the component file
Create a new file called decrypting-text-animation.jsx
in your reusable components folder (for example
/src/components/) and paste the following
code into it.
decrypting-text-animation.jsx
import { memo, useCallback, useEffect, useMemo, useState, Fragment } from "react";
import styles from "./decrypting-text-animation.module.css";
const random = (n = 1) => {
return Math.floor(Math.random() * n);
};
const DecryptingTextAnimation = (props) => {
const {
text,
speed = 50,
charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890!@#$%&*-+?",
className,
...restProps
} = props;
const letterVariants = ["outlined", "filled"];
const getRandomLetterVariant = useCallback(() => {
return letterVariants[random(letterVariants.length)];
}, []);
const getTextMappings = () => {
return (
text
?.split(" ")
.filter(Boolean)
.map(word => (
word
.split("")
.map((letter) => ({
letter,
variant: getRandomLetterVariant(),
index: random(charset.length),
}))
))
);
};
const [currentText, setCurrentText] = useState(text);
const [textMapping, setTextMapping] = useState(getTextMappings);
const shuffledCharset = useMemo(() => {
return charset.split("").sort(() => (
random(5) - random(5)
)).join("");
}, [charset, text]);
useEffect(() => {
const intervalId = setInterval(() => {
setTextMapping((prev) => {
const areAllDone = prev.flat().every(entry => {
return entry.letter === shuffledCharset[entry.index];
});
if(areAllDone) {
clearInterval(intervalId);
}
return prev.map(word => (
word.map(entry => {
const isDone = entry.letter === shuffledCharset[entry.index];
return {
...entry,
variant: isDone ? "" : getRandomLetterVariant(),
index: isDone ? (
entry.index
) : (
(entry.index + 1) % shuffledCharset.length
)
};
})
))
});
}, speed);
return () => {
clearInterval(intervalId);
};
}, [shuffledCharset, speed, getRandomLetterVariant]);
if (currentText !== text) {
setCurrentText(text);
setTextMapping(getTextMappings());
}
return (
<span
{...restProps}
className={[
className,
styles["decrypting-text"],
].join(" ")}
>
{
textMapping.map((word, wordIndex, arr) => (
<Fragment key={`word-${wordIndex}`}>
<span
aria-hidden={true}
className={styles["word"]}
>
{word.map((letter, letterIndex) => {
return (
<span
key={`letter-${wordIndex}-${letterIndex}`}
aria-hidden={true}
className={[
styles["letter"],
styles[`letter-${letter.variant}`]
].join(" ")}
>
{shuffledCharset[letter.index]}
</span>
);
})}
</span>
{wordIndex !== (arr.length - 1) && (
<span aria-hidden={true}>
</span>
)}
</Fragment>
))
}
<span
className={styles["sr-only"]}
>
{currentText}
</span>
</span>
)
};
export default memo(DecryptingTextAnimation); 2. Copy the CSS module file
In the same folder, create a file called decrypting-text-animation.module.css and paste the following
CSS.
decrypting-text-animation.module.css
.decrypting-text {
position: relative;
.word {
white-space: nowrap;
word-break: keep-all;
.letter {
display: inline-block;
font: inherit;
transition: all 100ms ease-in-out;
&.letter-filled {
background: currentColor;
}
&.letter-outlined {
box-shadow: inset 0 0 1px 1px currentColor;
}
}
}
> .sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
animation: none;
}
} Props
Configure the component with the following props:
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| text | string | Yes | - | The text content to be decrypted and displayed. All characters must exist in the specified charset. If the text includes characters outside this charset, a custom charset prop must be provided. |
| speed | number | No | 50 | Speed in milliseconds between each decrypting step. |
| charset | string | No | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890!@#$%&*-+?" | The character set used to generate random decrypting characters. |
| className | string | No | — | Optional class name applied to the root container. |
| style | React.CSSProperties | No | — | Inline styles applied to the root container. |