(function () { const DEFAULT_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*'; const DEFAULT_SPEED = 40; // ms per frame class TextScramble { constructor(el) { this.el = el; this.originalText = el.innerText; this.chars = el.dataset.scrambleChars || DEFAULT_CHARS; this.speed = parseInt(el.dataset.scrambleSpeed, 10) || DEFAULT_SPEED; this.frameRequest = null; this.isScrambling = false; } scramble() { if (this.isScrambling) return; this.isScrambling = true; const text = this.originalText; const length = text.length; let iteration = 0; const totalFrames = length * 2; // how long the scramble lasts cancelAnimationFrame(this.frameRequest); const tick = () => { this.el.innerText = text .split('') .map((char, i) => { if (char === ' ') return ' '; // preserve spaces if (i < iteration / 2) return char; // reveal from left return this.chars[Math.floor(Math.random() * this.chars.length)]; }) .join(''); if (iteration < totalFrames) { iteration++; this.frameRequest = setTimeout(() => requestAnimationFrame(tick), this.speed); } else { this.el.innerText = text; // ensure original text is restored this.isScrambling = false; } }; requestAnimationFrame(tick); } reset() { cancelAnimationFrame(this.frameRequest); clearTimeout(this.frameRequest); this.el.innerText = this.originalText; this.isScrambling = false; } } function init() { const targets = document.querySelectorAll('[data-scramble]'); targets.forEach((el) => { const scrambler = new TextScramble(el); el.style.cursor = 'default'; // optional: keeps cursor clean el.addEventListener('mouseenter', () => scrambler.scramble()); // Optional: reset on mouse leave mid-scramble // el.addEventListener('mouseleave', () => scrambler.reset()); }); } // Run after DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();

Ragana Geno

(function () { const DEFAULT_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*'; const DEFAULT_SPEED = 40; // ms per frame class TextScramble { constructor(el) { this.el = el; this.originalText = el.innerText; this.chars = el.dataset.scrambleChars || DEFAULT_CHARS; this.speed = parseInt(el.dataset.scrambleSpeed, 10) || DEFAULT_SPEED; this.frameRequest = null; this.isScrambling = false; } scramble() { if (this.isScrambling) return; this.isScrambling = true; const text = this.originalText; const length = text.length; let iteration = 0; const totalFrames = length * 2; // how long the scramble lasts cancelAnimationFrame(this.frameRequest); const tick = () => { this.el.innerText = text .split('') .map((char, i) => { if (char === ' ') return ' '; // preserve spaces if (i < iteration / 2) return char; // reveal from left return this.chars[Math.floor(Math.random() * this.chars.length)]; }) .join(''); if (iteration < totalFrames) { iteration++; this.frameRequest = setTimeout(() => requestAnimationFrame(tick), this.speed); } else { this.el.innerText = text; // ensure original text is restored this.isScrambling = false; } }; requestAnimationFrame(tick); } reset() { cancelAnimationFrame(this.frameRequest); clearTimeout(this.frameRequest); this.el.innerText = this.originalText; this.isScrambling = false; } } function init() { const targets = document.querySelectorAll('[data-scramble]'); targets.forEach((el) => { const scrambler = new TextScramble(el); el.style.cursor = 'default'; // optional: keeps cursor clean el.addEventListener('mouseenter', () => scrambler.scramble()); // Optional: reset on mouse leave mid-scramble // el.addEventListener('mouseleave', () => scrambler.reset()); }); } // Run after DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();