diff --git a/Jellyfin.Plugin.Seasonals/Web/easter.css b/Jellyfin.Plugin.Seasonals/Web/easter.css index fcb6dd7..a0a606f 100644 --- a/Jellyfin.Plugin.Seasonals/Web/easter.css +++ b/Jellyfin.Plugin.Seasonals/Web/easter.css @@ -53,7 +53,6 @@ left: 0; width: 160px; height: auto; - z-index: 1002; will-change: transform; } diff --git a/Jellyfin.Plugin.Seasonals/Web/easter.js b/Jellyfin.Plugin.Seasonals/Web/easter.js index 8562d76..8ee1968 100644 --- a/Jellyfin.Plugin.Seasonals/Web/easter.js +++ b/Jellyfin.Plugin.Seasonals/Web/easter.js @@ -2,7 +2,7 @@ const config = window.SeasonalsPluginConfig?.Easter || {}; const easter = config.EnableEaster !== undefined ? config.EnableEaster : true; const enableBunny = config.EnableBunny !== undefined ? config.EnableBunny : true; -/* MARK: BUNNY LOCOMOTION CONFIGURATION */ +/* MARK: Bunny movement config */ const jumpDistanceVw = 5; // Distance in vw the bunny covers per jump const jumpDurationMs = 770; // Time in ms the bunny spends moving during a jump const pauseDurationMs = 116.6666; // Time in ms the bunny pauses between jumps @@ -29,6 +29,39 @@ const easterEggImages = [ "../Seasonals/Resources/easter_images/eggs.png" ]; + +// Check visibility +function toggleEaster() { + const easterContainer = document.querySelector('.easter-container'); + if (!easterContainer) return; + + const videoPlayer = document.querySelector('.videoPlayerContainer'); + const trailerPlayer = document.querySelector('.youtubePlayerContainer'); + const isDashboard = document.body.classList.contains('dashboardDocument'); + const hasUserMenu = document.querySelector('#app-user-menu'); + + if (videoPlayer || trailerPlayer || isDashboard || hasUserMenu) { + easterContainer.style.display = 'none'; + if (rabbitTimeout) { + clearTimeout(rabbitTimeout); + isAnimating = false; // Reset to allow restarting later + } + } else { + easterContainer.style.display = 'block'; + if (!isAnimating && enableBunny) { + animateRabbit(document.querySelector('#rabbit')); + } + } +} +const observer = new MutationObserver(toggleEaster); + +observer.observe(document.body, { + childList: true, + subtree: true, + attributes: true +}); + + function createEasterGrassAndEggs(container) { let grassContainer = container.querySelector('.easter-grass-container'); if (!grassContainer) { @@ -164,16 +197,12 @@ function animateRabbit(rabbit) { function animationStep(timestamp) { if (!document.querySelector('.easter-container') || rabbit.style.display === 'none') { isAnimating = false; - // When hidden, we stop animating. The toggle function will re-init. return; } if (!startTime) { startTime = timestamp; - // FORCE GIF RESTART: - // The GIF must restart exactly when the jump loop starts. - // Re-assigning the src without a cache-busting timestamp resets it to frame 0 - // while still allowing the browser to serve the cached file immediately. + // resetting gif, forces the browser to restart the GIF from the first frame (crucial for syncing hops with movement) const currSrc = rabbit.src.split('?')[0]; rabbit.src = ''; rabbit.src = currSrc; @@ -181,7 +210,6 @@ function animateRabbit(rabbit) { const elapsed = timestamp - startTime; - // Calculate how many full loops (jump+pause) have happened const completedLoops = Math.floor(elapsed / loopDurationMs); const timeInCurrentLoop = elapsed % loopDurationMs; @@ -197,14 +225,11 @@ function animateRabbit(rabbit) { currentX = startX + (completedLoops * jumpDistanceVw + currentLoopDistance) * direction; - // Update DOM without CSS transitions (since we control it per-frame) + // Update DOM without CSS transitions rabbit.style.transform = `translateX(${currentX}vw) ${transformScale}`; // Check if finished crossing if ((direction === 1 && currentX >= endX) || (direction === -1 && currentX <= endX)) { - // Finished crossing! We can rest now. - // Since we explicitly reset the GIF to frame 0 at the start of every loop, - // we no longer have to snap the rest time to the loop duration mathematically. let restTime = Math.random() * (maxBunnyRestTime - minBunnyRestTime) + minBunnyRestTime; isAnimating = false; @@ -221,33 +246,6 @@ function animateRabbit(rabbit) { rabbitTimeout = requestAnimationFrame(animationStep); } -// Check visibility -function toggleEaster() { - const easterContainer = document.querySelector('.easter-container'); - if (!easterContainer) return; - - const videoPlayer = document.querySelector('.videoPlayerContainer'); - const trailerPlayer = document.querySelector('.youtubePlayerContainer'); - const isDashboard = document.body.classList.contains('dashboardDocument'); - const hasUserMenu = document.querySelector('#app-user-menu'); - - if (videoPlayer || trailerPlayer || isDashboard || hasUserMenu) { - easterContainer.style.display = 'none'; - if (rabbitTimeout) { - clearTimeout(rabbitTimeout); - isAnimating = false; // Reset to allow restarting later - } - } else { - easterContainer.style.display = 'block'; - if (!isAnimating && enableBunny) { - animateRabbit(document.querySelector('#rabbit')); - } - } -} - -const observer = new MutationObserver(toggleEaster); -observer.observe(document.body, { childList: true, subtree: true, attributes: true }); - function initializeEaster() { if (!easter) return;