diff --git a/ChessCubing.App/wwwroot/js/chesscubing-interop.js b/ChessCubing.App/wwwroot/js/chesscubing-interop.js index 0fceb13..71d2499 100644 --- a/ChessCubing.App/wwwroot/js/chesscubing-interop.js +++ b/ChessCubing.App/wwwroot/js/chesscubing-interop.js @@ -3,6 +3,9 @@ let viewportStarted = false; let audioContext = null; let authModalMessageHandler = null; + let menuScrollStarted = false; + let menuLastScrollY = 0; + let menuAnimationFrame = 0; function syncViewportHeight() { const visibleHeight = window.visualViewport?.height ?? window.innerHeight; @@ -38,6 +41,63 @@ window.requestAnimationFrame(() => window.requestAnimationFrame(syncViewportHeight)); } + function setMenuHidden(hidden) { + document.body.classList.toggle("site-menu-hidden", hidden); + } + + function syncMenuVisibility() { + menuAnimationFrame = 0; + + const menu = document.querySelector(".site-menu-shell"); + if (!menu) { + setMenuHidden(false); + menuLastScrollY = window.scrollY || window.pageYOffset || 0; + return; + } + + const modalIsOpen = document.querySelector(".modal:not(.hidden)"); + if (modalIsOpen) { + setMenuHidden(false); + menuLastScrollY = window.scrollY || window.pageYOffset || 0; + return; + } + + const currentScrollY = Math.max(window.scrollY || window.pageYOffset || 0, 0); + const delta = currentScrollY - menuLastScrollY; + + if (currentScrollY <= 24 || delta < -10) { + setMenuHidden(false); + } else if (delta > 10 && currentScrollY > 120) { + setMenuHidden(true); + } + + menuLastScrollY = currentScrollY; + } + + function queueMenuVisibilitySync() { + if (menuAnimationFrame) { + return; + } + + menuAnimationFrame = window.requestAnimationFrame(syncMenuVisibility); + } + + function startMenuScrollTracking() { + if (menuScrollStarted) { + queueMenuVisibilitySync(); + return; + } + + menuScrollStarted = true; + menuLastScrollY = window.scrollY || window.pageYOffset || 0; + window.addEventListener("scroll", queueMenuVisibilitySync, { passive: true }); + window.addEventListener("pageshow", queueMenuVisibilitySync); + window.addEventListener("resize", queueMenuVisibilitySync); + window.addEventListener("hashchange", queueMenuVisibilitySync); + window.setTimeout(queueMenuVisibilitySync, 0); + window.setTimeout(queueMenuVisibilitySync, 150); + } + function getAudioContext() { const AudioContextClass = window.AudioContext || window.webkitAudioContext; if (!AudioContextClass) { @@ -114,6 +174,11 @@ start: startViewport, }; + window.chesscubingMenu = { + start: startMenuScrollTracking, + sync: queueMenuVisibilitySync, + }; + window.chesscubingStorage = { getMatchState(storageKey, windowNameKey) { try { @@ -246,4 +311,5 @@ }; startViewport(); + startMenuScrollTracking(); })(); diff --git a/styles.css b/styles.css index 3500b1b..e335631 100644 --- a/styles.css +++ b/styles.css @@ -1035,6 +1035,10 @@ body[data-page="cube"] .zone-button.cube-hold-ready::after { width: 100%; margin: 0; padding: var(--safe-top) 0 0; + transition: + transform 220ms ease, + opacity 220ms ease; + will-change: transform, opacity; } .site-menu-bar { @@ -1044,6 +1048,12 @@ body[data-page="cube"] .zone-button.cube-hold-ready::after { backdrop-filter: blur(18px); } +body.site-menu-hidden .site-menu-shell { + transform: translateY(-100%); + opacity: 0; + pointer-events: none; +} + .site-menu-main { display: grid; grid-template-columns: auto minmax(0, 1fr) auto;