My personal website
at main 131 lines 4.9 kB view raw
1/*! 2 * Personal website of Sefa Eyeoglu 3 * Copyright (C) 2018-2022 Sefa Eyeoglu <contact@scrumplex.net> 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU Affero General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU Affero General Public License for more details. 14 * 15 * You should have received a copy of the GNU Affero General Public License 16 * along with this program. If not, see <https://www.gnu.org/licenses/>. 17 */ 18 19import ready from "./_utils"; 20 21const mainElem = document.getElementById("main"); 22 23document.querySelectorAll(".scrumplex-logo").forEach((elem) => { 24 elem.addEventListener("dblclick", () => { 25 const randomRotation = Math.floor(Math.random() * 360); 26 elem.style.transform = `rotate(${randomRotation}deg)`; 27 }); 28}); 29 30document.querySelectorAll("*[data-scroll]").forEach((elem) => { 31 elem.addEventListener("click", (e) => { 32 e.preventDefault(); 33 const targetSelector = elem.getAttribute("data-scroll"); 34 applyScrollSpecial(targetSelector); 35 }); 36}); 37 38mainElem.addEventListener("animationend", applyScrollInitially); 39 40function applyScrollInitially() { 41 mainElem.removeEventListener("animationend", applyScrollInitially); 42 43 window.addEventListener("scroll", applyScrollConditionally); 44 45 if (location.hash.startsWith("#")) { 46 const result = applyScrollSpecial(location.hash); 47 if (result) return; 48 } 49 applyScrollConditionally(); 50} 51 52function applyScrollConditionally() { 53 if ( 54 mainElem.getBoundingClientRect().top <= 0 || // offset to top window border 55 mainElem.getBoundingClientRect().height >= window.innerHeight 56 ) 57 applyScroll(); 58} 59 60function applyScrollSpecial(targetSelector) { 61 if (!targetSelector) return false; 62 const targetElem = document.querySelector(targetSelector); 63 if (!targetElem) return false; 64 const offset = applyScroll(targetElem); 65 window.scrollTo({ top: offset, behavior: "smooth" }); 66 history.pushState({ hash: targetSelector }, "", targetSelector); 67 return true; 68} 69 70function applyScroll(scrollTarget = null) { 71 // Black magic ahead! 72 // We sometimes need this method to show all sheets and then scroll to one of them. 73 // To achieve this we need to have a way to get the target position (like pixels) of 74 // the sheet we want to scroll to (in this case it's scrollTarget). 75 // As the bounding box always includes current transforms (so our translateY in the 76 // fade-in animation) we need to somehow get the boundingBox while our sheet is not 77 // being transformed. A hacky way to do this is to modify the behavior of .sheet[hidden] 78 // depending on the state of this method. In this case we are adding .scrolled to body 79 // which will cause all hidden sheets to instead be transparent. 80 // Additionally we will stop any animations that would be playing then. 81 // Now the element should be at it's target position so we just grab the position before 82 // un-hiding the elements and voila! 83 // One caveat: if the content changes after this method returned, the scroll might be a 84 // bit offset. 85 86 let offset = getElementScrollOffset(scrollTarget); 87 if (document.body.classList.contains("scrolled")) return offset; 88 89 const hiddenElems = document.querySelectorAll(".sheet[hidden]"), 90 posMain = getElementScrollOffset(mainElem); // offset to top rel. to document 91 92 document.getElementById("wrapper").style.paddingTop = `${posMain}px`; 93 document.body.classList.add("scrolled"); 94 95 mainElem.classList.add("sheet-splashed"); 96 mainElem.classList.remove("sheet-splash"); 97 if (scrollTarget) 98 // recalculate, as it probably changed because of body.scrolled 99 offset = getElementScrollOffset(scrollTarget); 100 101 hiddenElems.forEach((elem) => { 102 elem.removeAttribute("hidden"); 103 }); 104 105 window.removeEventListener("scroll", applyScrollConditionally); 106 return offset; 107} 108 109function startSpinningChars(fps = 4) { 110 const spinningChars = ["|", "/", "-", "\\"], 111 spinnerElement = document.getElementById("text-spinner"); 112 113 let currentIndex = 0; 114 115 setInterval(() => { 116 spinnerElement.innerText = spinningChars[currentIndex]; 117 currentIndex++; 118 if (currentIndex >= spinningChars.length) currentIndex = 0; 119 }, 1000 / fps); 120 121 spinnerElement.removeAttribute("hidden"); 122} 123 124function getElementScrollOffset(elem) { 125 if (elem) return window.pageYOffset + elem.getBoundingClientRect().top; 126 return -1; 127} 128 129ready().then(() => { 130 startSpinningChars(); 131});