diff --git a/public/scripts/text-scramble.js b/public/scripts/text-scramble.js index aa0ad91..bcebb80 100644 --- a/public/scripts/text-scramble.js +++ b/public/scripts/text-scramble.js @@ -1,99 +1,105 @@ function applyScrambleEffect() { const glitchTextOnceElements = document.querySelectorAll('.glitch-text'); const glitchTextRepeatElements = document.querySelectorAll('.glitch-text-interval'); + const glitchTextHoverElements = document.querySelectorAll('.glitch-text-hover'); + const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + function animateScramble(element, text, duration = 1000) { - const chars = []; - element.innerHTML = ''; - const preScrambleWidth = element.offsetWidth; - element.style.width = `${preScrambleWidth}px`; - - for (let t = 0; t < text.length; t++) { - const span = document.createElement('span'); - span.innerHTML = text[t] === ' ' ? ' ' : text[t]; - chars[t] = span; - span.style.display = 'inline-block'; - element.appendChild(span); - } - - const rand = Math.random; - const SECONDS = 1000; - const FPS = 30; - const animationLength = duration; - - function animate3(k) { - const kk = k * text.length; - for (let i = 0; i < text.length; i++) { - if (kk < i) { - chars[i].innerHTML = String.fromCharCode(~~(65 + rand() * 26)); - } else { - chars[i].innerHTML = text[i] === ' ' ? ' ' : text[i]; - } + const chars = []; + element.innerHTML = ''; + const preScrambleWidth = element.offsetWidth; + element.style.width = `${preScrambleWidth}px`; + + for (let t = 0; t < text.length; t++) { + const span = document.createElement('span'); + span.innerHTML = text[t] === ' ' ? ' ' : text[t]; + chars[t] = span; + span.style.display = 'inline-block'; + element.appendChild(span); } - } - - let start = Date.now(); - function animate() { - const current = Date.now(); - const time = current - start; - const k = time / animationLength; - - if (k < 1) { - setTimeout(animate, SECONDS / FPS); - animate3(k); - } else { - for (let i = 0; i < text.length; i++) { - chars[i].innerHTML = text[i] === ' ' ? ' ' : text[i]; - } - element.style.width = 'auto'; - element.isAnimating = false; + + const rand = Math.random; + const SECONDS = 1000; + const FPS = 30; + const animationLength = duration; + + function animate3(k) { + const kk = k * text.length; + for (let i = 0; i < text.length; i++) { + if (kk < i) { + chars[i].innerHTML = charset[Math.floor(rand() * charset.length)]; + } else { + chars[i].innerHTML = text[i] === ' ' ? ' ' : text[i]; + } + } } - } - - animate(); + + let start = Date.now(); + function animate() { + const current = Date.now(); + const time = current - start; + const k = time / animationLength; + + if (k < 1) { + setTimeout(animate, SECONDS / FPS); + animate3(k); + } else { + for (let i = 0; i < text.length; i++) { + chars[i].innerHTML = text[i] === ' ' ? ' ' : text[i]; + } + element.style.width = 'auto'; + element.isAnimating = false; + } + } + + animate(); } - + + // Use IntersectionObserver for once-only animation const observer = new IntersectionObserver((entries, observer) => { - entries.forEach(entry => { - if (entry.isIntersecting) { - const element = entry.target; - const text = element.innerText; - animateScramble(element, text); - observer.unobserve(element); - } - }); + entries.forEach(entry => { + if (entry.isIntersecting) { + const element = entry.target; + const text = element.innerText; + animateScramble(element, text); + observer.unobserve(element); + } + }); }, { - threshold: 0.1 + threshold: 0.1 }); - + glitchTextOnceElements.forEach((element) => { - observer.observe(element); + observer.observe(element); }); - + glitchTextRepeatElements.forEach((element) => { - const text = element.innerText; - - animateScramble(element, text); - - const intervalId = setInterval(() => { - if (!element.isAnimating) { - animateScramble(element, text); - } - }, 5000); - - element.addEventListener('mouseenter', () => { - if (!element.isAnimating) { - clearInterval(intervalId); - animateScramble(element, text, 800); - element.isAnimating = true; - - setTimeout(() => { - element.isAnimating = false; - }, 1000); - } - }); + const text = element.innerText; + + animateScramble(element, text); + + const intervalId = setInterval(() => { + if (!element.isAnimating) { + animateScramble(element, text); + } + }, 5000); }); - } - - document.addEventListener('DOMContentLoaded', applyScrambleEffect); - \ No newline at end of file + + // Hover functionality for `.glitch-text-hover` elements + glitchTextHoverElements.forEach((element) => { + const text = element.innerText; + + element.addEventListener('mouseenter', () => { + // Only start the animation if it isn't already in progress + if (!element.isAnimating) { + element.isAnimating = true; + animateScramble(element, text, 800); + } + }); + + // No need for 'mouseleave' as the animation will complete even if the mouse leaves + }); +} + +document.addEventListener('DOMContentLoaded', applyScrambleEffect); diff --git a/src/components/AboutFooter.astro b/src/components/AboutFooter.astro index 2d656be..0afa6a7 100644 --- a/src/components/AboutFooter.astro +++ b/src/components/AboutFooter.astro @@ -111,7 +111,7 @@ const sortedSections = sectionsConfig {section.name === "contributors" && (