function waitForAnimationEnd(elem, animation_name) { return new Promise(resolve => { elem.onanimationend = (event) => { if(event.animationName === animation_name) { elem.onanimationend = null; resolve(); } }; }); } let sound = new Audio("../assets/game-boy-advance-startup-sound.mp3"); sound.load(); async function sessionStartupAnimation() { // Page setup let all_default_elements = document.body.querySelectorAll("*"); all_default_elements.forEach((elem) => elem.style.opacity = 0); // Get quotes possibilities // Not the most optimised way, but it's working let xmlhttp = new XMLHttpRequest(); xmlhttp.open("GET", "assets/startup_quotes.json", false); xmlhttp.send(); if(xmlhttp.status != 200) { throw new WebTransportError({ message: "The file 'assets/startup_quotes.json' is not available", streamErrorCode: xmlhttp.status }); } let quotes = JSON.parse(xmlhttp.responseText)["quotes"]; let quote = quotes[Math.floor(Math.random() * quotes.length)]; // First the author let author_elem = document.createElement("h2"); author_elem.className = "startup-animation-author"; author_elem.innerHTML = quote.author + "®"; document.body.appendChild(author_elem); await waitForAnimationEnd(author_elem, "opacity-fade-in") // Then the text and sound let text_elem = document.createElement("h1"); text_elem.className = "startup-animation-text"; let time_between_chars = 200 / quote.text.length; // ms for(let i = 0; i < quote.text.length; i++) { let char_elem = document.createElement("div"); char_elem.className = "startup-animation-char" char_elem.style.animation = "char-animation 3s ease-in-out " + String(i * time_between_chars) + "ms, opacity-fade-in 1s " + String(i * time_between_chars) + "ms"; if(quote.text[i] == " ") char_elem.innerHTML = "
" + quote.text[i] + "
"; else char_elem.textContent = quote.text[i]; console.log(char_elem.onanimationend); text_elem.appendChild(char_elem); } document.body.appendChild(text_elem); sound.play(); await waitForAnimationEnd(text_elem.firstChild, "opacity-fade-in"); text_elem.childNodes.forEach((char_elem) => char_elem.style.opacity = 1); await waitForAnimationEnd(text_elem.lastChild, "char-animation"); // Fade out for both text_elem.style.animation = "opacity-fade-out 1s ease-in"; text_elem.style.opacity = 0; author_elem.style.animation = "opacity-fade-out 1s ease-in"; author_elem.style.opacity = 0; await waitForAnimationEnd(text_elem, "opacity-fade-out"); // Remove them and fade in for page content document.body.removeChild(author_elem); document.body.removeChild(text_elem); all_default_elements.forEach((elem) => elem.style.animation = "opacity-fade-in 0.5s"); all_default_elements.forEach((elem) => elem.style.opacity = 1); window.onkeydown = null; } function interruptStartingAnimation() { let author_elem = document.body.querySelector(".startup-animation-author"); author_elem.getAnimations().forEach((animation) => { // Skip if fade-out if(animation.animationName != "opacity-fade-out") animation.finish(); }); let text_elem = document.body.querySelectorAll(".startup-animation-char"); text_elem.forEach((char_elem) => char_elem.getAnimations().forEach((animation) => { // No need to skip fade-out because it is handled by startup-animation-text if(animation.currentTime < 2800) animation.currentTime = 2800; })); if(sound.currentTime < 2.75) { sound.currentTime = 2.75; } } window.onkeydown = () => { interruptStartingAnimation(); } // Only start animation once per session window.onload = () => { document.body.style.opacity = 1; if(!sessionStorage.getItem("hasExecutedSessionStartupAnimation")) { sessionStorage.setItem("hasExecutedSessionStartupAnimation", "true"); sessionStartupAnimation(); } }