@twistezo/react-text-scramble
Version:
React text scramble effect
144 lines (142 loc) • 3.82 kB
JavaScript
// 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);
}
export {
createTextScramble,
TextScrambleAnimator,
SYMBOLS,
DEFAULT_PAUSE_TIME,
DEFAULT_PAUSED,
DEFAULT_NEXT_LETTER_SPEED,
DEFAULT_LETTER_SPEED
};