UNPKG

@twistezo/react-text-scramble

Version:
192 lines (187 loc) 4.91 kB
// src/TextScramble.tsx import { useEffect, useRef } from "react"; // src/constants.ts var DEFAULT_LETTER_SPEED = 5; var DEFAULT_NEXT_LETTER_SPEED = 100; var DEFAULT_PAUSE_TIME = 1500; var DEFAULT_PAUSED = false; var SYMBOLS = "!<>-_\\/[]{}—=+*^?#".split(""); // src/utils.ts var randomItem = (array) => array[Math.floor(Math.random() * array.length)]; var nextItem = (array, currentItem) => { const currentIndex = array.indexOf(currentItem); const bound = array.length; const nextIndex = (currentIndex + bound + 1) % bound; return array[nextIndex]; }; // src/TextScrambleAnimator.ts class TextScrambleAnimator { bakeLetterInterval = null; bakeTextInterval = null; currentText; displayedText; element; leftIndexes = []; letterSpeed; nextLetterSpeed; paused; pauseTime; pauseTimeout = null; texts; constructor(element, options) { this.element = element; this.texts = options.texts; this.letterSpeed = options.letterSpeed ?? DEFAULT_LETTER_SPEED; this.nextLetterSpeed = options.nextLetterSpeed ?? DEFAULT_NEXT_LETTER_SPEED; this.pauseTime = options.pauseTime ?? DEFAULT_PAUSE_TIME; this.paused = options.paused ?? DEFAULT_PAUSED; this.currentText = this.texts[0]; this.displayedText = this.initSymbols(); this.render(); if (!this.paused) { this.animate(); } } destroy() { this.clearAllIntervals(); } pause() { this.paused = true; if (this.pauseTimeout) { clearTimeout(this.pauseTimeout); this.pauseTimeout = null; } } play() { this.paused = false; this.clearAllIntervals(); this.animate(); } reset() { this.clearAllIntervals(); this.currentText = this.texts[0]; this.displayedText = this.initSymbols(); this.render(); this.leftIndexes = []; if (!this.paused) { this.animate(); } } setTexts(texts) { this.texts = texts; this.reset(); } animate() { this.bakeText(); } bakeLetter() { this.bakeLetterInterval = setInterval(() => { if (!this.paused) { const updatedText = []; this.currentText.split("").forEach((char, i) => { if (!this.leftIndexes.includes(i)) { updatedText[i] = this.currentText[i]; return; } updatedText[i] = randomItem(SYMBOLS); }); this.displayedText = updatedText; this.render(); } }, this.letterSpeed); } bakeText() { this.leftIndexes = this.currentText.split("").map((_, i) => i); this.bakeLetter(); this.bakeTextInterval = setInterval(() => { if (!this.paused) { if (this.leftIndexes.length === 0) { this.clearAllIntervals(); this.pauseTimeout = setTimeout(() => { this.currentText = nextItem(this.texts, this.currentText); this.displayedText = this.initSymbols(); this.render(); this.animate(); }, this.pauseTime); return; } this.leftIndexes.shift(); } }, this.nextLetterSpeed); } clearAllIntervals() { if (this.bakeLetterInterval) { clearInterval(this.bakeLetterInterval); this.bakeLetterInterval = null; } if (this.bakeTextInterval) { clearInterval(this.bakeTextInterval); this.bakeTextInterval = null; } if (this.pauseTimeout) { clearTimeout(this.pauseTimeout); this.pauseTimeout = null; } } initSymbols() { return Array(this.currentText.length).fill(0).map(() => randomItem(SYMBOLS)); } render() { this.element.textContent = this.displayedText.join(""); } } function createTextScramble(element, options) { return new TextScrambleAnimator(element, options); } // src/TextScramble.tsx import { jsxDEV } from "react/jsx-dev-runtime"; var TextScramble = ({ className, letterSpeed, nextLetterSpeed, paused, pauseTime, texts }) => { const elementRef = useRef(null); const animatorRef = useRef(null); useEffect(() => { if (!elementRef.current) return; animatorRef.current = new TextScrambleAnimator(elementRef.current, { letterSpeed, nextLetterSpeed, paused, pauseTime, texts }); return () => { animatorRef.current?.destroy(); }; }, [texts, letterSpeed, nextLetterSpeed, pauseTime]); useEffect(() => { if (!animatorRef.current) return; if (paused) { animatorRef.current.pause(); } else { animatorRef.current.play(); } }, [paused]); return /* @__PURE__ */ jsxDEV("div", { className, ref: elementRef }, undefined, false, undefined, this); }; var TextScramble_default = TextScramble; export { TextScramble_default as default, createTextScramble, TextScrambleAnimator, TextScramble_default as TextScramble, SYMBOLS, DEFAULT_PAUSE_TIME, DEFAULT_PAUSED, DEFAULT_NEXT_LETTER_SPEED, DEFAULT_LETTER_SPEED };