feat: name scramble

Signed-off-by: Ameya Shenoy <shenoy.ameya@gmail.com>
This commit is contained in:
Ameya Shenoy 2025-06-08 00:32:01 +05:30
parent bbc40ec297
commit f12356ae66
2 changed files with 111 additions and 9 deletions

View file

@ -1,16 +1,14 @@
import * as React from "react";
import TextScramble from "@/components/TextScramble";
export function NameComponent() {
return (
<div className="flex justify-center font-[family-name:var(--font-spacegrotesk-sans)] font-semibold text-fluid tracking-tighter">
<p>
<span className="selection:bg-red-500">Ameya </span>
<span
className={`text-red-500 selection:bg-black dark:selection:bg-white`}
>
Shenoy
</span>
</p>
<TextScramble
firstName="Ameya"
lastName="Shenoy"
onlineHandleFirstName="coding"
onlineHandleLastName="coffee"
/>
</div>
);
}

View file

@ -0,0 +1,104 @@
"use client";
import { useState, useEffect, useCallback } from "react";
interface TextScrambleProps {
firstName: string;
lastName: string;
onlineHandleFirstName: string;
onlineHandleLastName: string;
}
const TextScramble: React.FC<TextScrambleProps> = ({
firstName,
lastName,
onlineHandleFirstName,
onlineHandleLastName,
}) => {
const [displayFirstName, setfirstNameText] = useState(firstName);
const [displayLastNameText, setLastNameText] = useState(lastName);
const [isHovering, setIsHovering] = useState(false);
const chars = "!<>-_\\/[]{}—=+*^?#________";
const scramble = useCallback(
(fromText: string, toText: string, setFunctiono) => {
let counter = 0;
const length = Math.max(fromText.length, toText.length);
const queue = [];
// Pad texts with spaces if they have different lengths
fromText = fromText.padEnd(length, " ");
toText = toText.padEnd(length, " ");
for (let i = 0; i < length; i++) {
const fromChar = fromText[i];
const toChar = toText[i];
const start = Math.floor(Math.random() * 40);
const end = start + Math.floor(Math.random() * 40);
queue.push({ fromText: fromChar, toText: toChar, start, end });
}
const update = () => {
let output = "";
let complete = 0;
for (let i = 0; i < length; i++) {
const { fromText, toText, start, end } = queue[i];
const char =
counter >= end
? toText
: counter >= start
? chars[Math.floor(Math.random() * chars.length)]
: fromText;
output += char;
if (counter >= end) complete++;
}
setFunctiono(output);
if (complete < length) {
requestAnimationFrame(update);
counter++;
}
};
requestAnimationFrame(update);
},
[],
);
useEffect(() => {
if (isHovering) {
scramble(firstName, onlineHandleFirstName, setfirstNameText);
scramble(lastName, onlineHandleLastName, setLastNameText);
} else {
scramble(displayFirstName, firstName, setfirstNameText);
scramble(displayLastNameText, lastName, setLastNameText);
}
}, [isHovering, scramble, firstName, lastName]);
return (
<div
className="group relative font-mono transition-all duration-300"
onMouseEnter={() => setIsHovering(true)}
onMouseLeave={() => setIsHovering(false)}
>
<span className="inline-block group-hover:opacity-0 transition-opacity">
<span className="selection:bg-red-500">{displayFirstName}</span>
<span className="text-red-500 selection:bg-black dark:selection:bg-white">
{displayLastNameText}
</span>
</span>
<span className="absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity">
<span className="selection:bg-red-500">{displayFirstName}</span>
<span className="text-red-500 selection:bg-black dark:selection:bg-white">
{displayLastNameText}
</span>
</span>
</div>
);
};
export default TextScramble;