| | |
| | | * @returns {Promise<unknown>} |
| | | */ |
| | | window.typeText = function(container, text, speed = 50) { |
| | | container.classList.add('typeText'); |
| | | return new Promise((resolve) => { |
| | | // Cancel any existing animation on this container |
| | | if (container._typeInterval) { |
| | | clearInterval(container._typeInterval); |
| | | delete container._typeInterval; |
| | | } |
| | | |
| | | let index = 0; |
| | | container.textContent = ''; |
| | | |
| | | const interval = setInterval(() => { |
| | | container._typeInterval = setInterval(() => { |
| | | if (index < text.length) { |
| | | container.textContent += text.charAt(index); |
| | | index++; |
| | | } else { |
| | | clearInterval(interval); |
| | | clearInterval(container._typeInterval); |
| | | delete container._typeInterval; |
| | | resolve(); |
| | | } |
| | | }, speed); |
| | |
| | | } |
| | | |
| | | /** |
| | | * Erases text like a keyboard would. TODO: erase a set word from existing text |
| | | * Erases text like a keyboard would. |
| | | * @param container |
| | | * @param speed |
| | | * @returns {Promise<unknown>} |
| | | */ |
| | | window.eraseText = function(container, speed = 10) { |
| | | return new Promise((resolve) => { |
| | | // Cancel any existing animation on this container |
| | | if (container._eraseInterval) { |
| | | clearInterval(container._eraseInterval); |
| | | delete container._eraseInterval; |
| | | } |
| | | |
| | | let text = container.textContent; |
| | | let index = text.length; |
| | | |
| | | const interval = setInterval(() => { |
| | | container._eraseInterval = setInterval(() => { |
| | | if (index > 0) { |
| | | index--; |
| | | container.textContent = text.substring(0, index); |
| | | } else { |
| | | clearInterval(interval); |
| | | clearInterval(container._eraseInterval); |
| | | delete container._eraseInterval; |
| | | resolve(); |
| | | } |
| | | }, speed); |
| | |
| | | * @returns {Function} - Call this function to stop the loop |
| | | */ |
| | | window.typeLoop = function(container, text, typeSpeed = 50, eraseSpeed = 10, pauseAfterType = 1000, pauseAfterErase = 250) { |
| | | // Generate unique key for this container |
| | | const containerId = container.id || container.dataset.typeKey || `type-${Date.now()}`; |
| | | if (!container.dataset.typeKey) { |
| | | container.dataset.typeKey = containerId; |
| | | } |
| | | |
| | | // Stop any existing loop immediately |
| | | if (container._stopTyping) { |
| | | container._stopTyping(); |
| | | } |
| | | |
| | | let isRunning = true; |
| | | |
| | | async function loop() { |
| | | while (isRunning) { |
| | | // Type the text |
| | | await window.typeText(container, text, typeSpeed); |
| | | |
| | | // Wait 1 second |
| | | if (!isRunning) break; |
| | | await new Promise(resolve => setTimeout(resolve, pauseAfterType)); |
| | | |
| | | // Erase the text |
| | | if (!isRunning) break; |
| | | await window.eraseText(container, eraseSpeed); |
| | | |
| | | // Wait 0.25 seconds before next iteration |
| | | if (!isRunning) break; |
| | | await new Promise(resolve => setTimeout(resolve, pauseAfterErase)); |
| | | } |
| | | } |
| | | |
| | | // Start the loop |
| | | loop(); |
| | | |
| | | // Return a function to stop the loop |
| | | return function stopLoop() { |
| | | const stopLoop = function() { |
| | | isRunning = false; |
| | | if (container._typeInterval) { |
| | | clearInterval(container._typeInterval); |
| | | delete container._typeInterval; |
| | | } |
| | | if (container._eraseInterval) { |
| | | clearInterval(container._eraseInterval); |
| | | delete container._eraseInterval; |
| | | } |
| | | }; |
| | | |
| | | container._stopTyping = stopLoop; |
| | | loop(); // Start immediately |
| | | |
| | | return stopLoop; |
| | | }; |
| | | |
| | | window.toCamelCase = function (string) { |