pelita-webtournament/app/typewritertext.tsx
2024-09-04 14:59:54 +02:00

142 lines
3.9 KiB
TypeScript

import React, { ReactNode, useEffect, useRef, useState } from "react";
import anime from "animejs/lib/anime.es.js";
import styles from "./typewritertext.module.css";
function Cursor() {
const self = useRef<HTMLDivElement>(null);
useEffect(() => {
const blink = anime({
targets: self.current,
loop: true,
duration: 750,
opacity: [{ value: [1, 1] }, { value: [0, 0] }],
});
}, []);
return (<span ref={self} className={`cursor ${styles.cursor}`}></span>)
}
function FadingLetter({ char }: { char: string }) {
const letter = useRef(null);
useEffect(() => {
anime({
targets: letter.current,
loop: false,
//opacity: [{ value: [1, 1] }, { value: [0, 0] }],
color: ['#ffffff', '#ffffff', '#ffffff', '#ffffff', '#eeeeee'],
duration: 100,
});
}, []);
return (<span ref={letter} className={`letter ${styles.letter}`}>
{char == " " ? "\u00A0" : char}
</span>);
}
function TypewriterLine( { text, cursor, lineFinished }: { text: string, cursor: boolean, lineFinished: () => any }) {
const TYPE_AFTER_MS = 1_000;
const JUMP_AFTER_MS = 80;
const [index, setIndex] = useState(0);
const [hasCursor, setHasCursor] = useState(true);
const current = text.slice(0, index) || "";
const lettersHtml = (!current) ? (<br/>) : current.split("").map((c, i) => {
return (<FadingLetter key={`letter,${i}`} char={c}></FadingLetter>);
});
const lineHtml = (
<>
<p className="letters">{lettersHtml}{
(hasCursor || cursor) ? (<Cursor></Cursor>) : null
}</p>
</>
);
const text_animation_ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const interval = setInterval(() => {
setIndex(oldIndex => {
if (oldIndex < text.length) {
return oldIndex + 1;
} else {
clearInterval(interval);
setHasCursor(false);
//lineFinished();
// const cursorRemove = setInterval(() => { setHasCursor(false); return () => clearInterval(cursorRemove); }, 500);
return oldIndex;
}
})
}, JUMP_AFTER_MS);
//Clearing the interval
return () => clearInterval(interval);
}, [index, text.length]);
if (current === "")
return (<div><br/></div>);
return (
<div ref={text_animation_ref} className={styles.text_animation}>
{lineHtml}
</div>
);
}
function TypewriterText({ text, lines }: { text: string[], lines: number }) {
// IMPORTANT: This code chokes on an empty string. Logic needs to be refactored
const len = text.length;
return text.map((v, idx) => <TypewriterLine key={idx} text={v} cursor={idx + 1 === len} lineFinished={() => {}}></TypewriterLine>).slice(-lines);
/*
const TYPE_AFTER_MS = 1_000;
const JUMP_AFTER_MS = 80;
const [index, setIndex] = useState(0);
const [hasCursor, setHasCursor] = useState(true);
const current = text.slice(0, index) || "";
const lettersHtml: ReactNode[] = (!current) ? (<br/>) : current.split("").map((c, i) => {
return (<FadingLetter key={`letter,${i}`} char={c}></FadingLetter>);
});
const lineHtml = (
<>
<div className="letters">{lettersHtml}{
hasCursor ? (<Cursor></Cursor>) : null
}</div>
</>
);
const text_animation_ref = useRef<HTMLDivElement>(null);
useEffect(() => {
//Implementing the setInterval method
const interval = setInterval(() => {
setIndex(oldIndex => {
if (oldIndex < text.length) {
return oldIndex + 1;
} else {
clearInterval(interval);
// const cursorRemove = setInterval(() => { setHasCursor(false); return () => clearInterval(cursorRemove); }, 500);
return oldIndex;
}
})
}, 100);
//Clearing the interval
return () => clearInterval(interval);
}, [index]);
return (
<div ref={text_animation_ref} className={styles.text_animation}>
<h1>{lineHtml}</h1>
</div>
);
*/
}
export default TypewriterText;