50 components available

Text Scramble Effect

November 2024
An animated text scrambling effect that reveals text character-by-character.
postgresujj@acf/~\+1g6@2rsa0=gypoyskwzt@oej\vvjldrb1=lt7ay10f0o+9
Playground
<TextScrambleEffect 
  text='postgresql://example@ep-938132.eu-central-1.aws.neon.tech/primary' 
/>
Source Code
MUI
Tailwind
text-scramble-effect.tsx
1import { useEffect, useRef, useState } from 'react';
2
3const CHARACTER_SET =
4 '0123456789qwertyuiopasdfghjklzxcvbnm!?></\\a~+*=@#$%'.split('');
5const getRandomIndex = () => Math.floor(Math.random() * CHARACTER_SET.length);
6const generateRandomString = (length: number) => {
7 let randomString = '';
8 for (let i = 0; i < length; i++) {
9 randomString += CHARACTER_SET[getRandomIndex()];
10 }
11 return randomString;
12};
13
14export interface TextScrambleEffectProps {
15 text?: string;
16}
17
18const TextScrambleEffect = (props: TextScrambleEffectProps) => {
19 const { text = '' } = props;
20
21 const [displayText, setDisplayText] = useState([text, '']);
22 const [isAnimationDisabled, setIsAnimationDisabled] = useState(false);
23
24 const animationFrameRef = useRef<number>();
25 const progressRef = useRef(0);
26
27 // Disable animation for small screens
28 // useEffect(() => {
29 // setIsAnimationDisabled(windowWidth <= 1100);
30 // }, [windowWidth]);
31
32 useEffect(() => {
33 if (isAnimationDisabled) return;
34
35 progressRef.current = 0;
36 const textLength = text.length;
37
38 const animateTextReveal = () => {
39 if (progressRef.current < 36) {
40 setDisplayText(['', generateRandomString(textLength)]);
41 } else if (progressRef.current / 2 - 18 < textLength) {
42 const revealedPart = text.slice(0, progressRef.current / 2 - 18 - 1);
43 const remainingRandomPart = generateRandomString(
44 textLength - revealedPart.length
45 );
46 setDisplayText([revealedPart, remainingRandomPart]);
47 } else {
48 setDisplayText([text, '']);
49 return;
50 }
51
52 progressRef.current += 2;
53 animationFrameRef.current = requestAnimationFrame(animateTextReveal);
54 };
55
56 animationFrameRef.current = requestAnimationFrame(animateTextReveal);
57
58 return () => cancelAnimationFrame(animationFrameRef.current!);
59 }, [text, isAnimationDisabled]);
60
61 useEffect(() => {
62 if (isAnimationDisabled) {
63 setDisplayText(() => ['', generateRandomString(text.length)]);
64 }
65 }, [text, isAnimationDisabled]);
66
67 useEffect(() => {
68 if (isAnimationDisabled) {
69 setDisplayText(() => [text, '']);
70 }
71 }, [isAnimationDisabled, text]);
72
73 return (
74 <div>
75 <span>{displayText[0]}</span>
76 <span>{displayText[1]}</span>
77 </div>
78 );
79};
80
81export default TextScrambleEffect;
Inspired by: https://neon.tech