feat: name scramble
Signed-off-by: Ameya Shenoy <shenoy.ameya@gmail.com>
This commit is contained in:
parent
bbc40ec297
commit
f12356ae66
2 changed files with 111 additions and 9 deletions
|
|
@ -1,16 +1,14 @@
|
||||||
import * as React from "react";
|
import TextScramble from "@/components/TextScramble";
|
||||||
|
|
||||||
export function NameComponent() {
|
export function NameComponent() {
|
||||||
return (
|
return (
|
||||||
<div className="flex justify-center font-[family-name:var(--font-spacegrotesk-sans)] font-semibold text-fluid tracking-tighter">
|
<div className="flex justify-center font-[family-name:var(--font-spacegrotesk-sans)] font-semibold text-fluid tracking-tighter">
|
||||||
<p>
|
<TextScramble
|
||||||
<span className="selection:bg-red-500">Ameya </span>
|
firstName="Ameya"
|
||||||
<span
|
lastName="Shenoy"
|
||||||
className={`text-red-500 selection:bg-black dark:selection:bg-white`}
|
onlineHandleFirstName="coding"
|
||||||
>
|
onlineHandleLastName="coffee"
|
||||||
Shenoy
|
/>
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
104
frontend/src/components/TextScramble.tsx
Normal file
104
frontend/src/components/TextScramble.tsx
Normal 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;
|
||||||
Loading…
Reference in a new issue