const santaIsFlying = true; // enable/disable santa let snowflakesCount = 500; // count of snowflakes (recommended values: 300-600) const snowflakesCountMobile = 250; // count of snowflakes on mobile devices const snowFallSpeed = 3; // speed of snowfall (recommended values: 0-5) let msgPrinted = false; // flag to prevent multiple console messages let canvas, ctx; // canvas and context for drawing snowflakes let animationFrameId; // ID of the animation frame // function to check and control the santa function toggleSnowfall() { const santaContainer = document.querySelector('.santa-container'); if (!santaContainer) 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'); // hide santa if video/trailer player is active or dashboard is visible if (videoPlayer || trailerPlayer || isDashboard || hasUserMenu) { santaContainer.style.display = 'none'; // hide santa removeCanvas(); if (!msgPrinted) { console.log('Snowfall hidden'); msgPrinted = true; } } else { santaContainer.style.display = 'block'; // show santa if (!animationFrameId) { initializeCanvas(); snowflakes = createSnowflakes(santaContainer); animateAll(); } else { console.warn('could not initialize santa: animation frame is already running'); } if (msgPrinted) { console.log('Snowfall visible'); msgPrinted = false; } } } // observe changes in the DOM const observer = new MutationObserver(toggleSnowfall); // start observation observer.observe(document.body, { childList: true, // observe adding/removing of child elements subtree: true, // observe all levels of the DOM tree attributes: true // observe changes to attributes (e.g. class changes) }); function initializeCanvas() { if (document.getElementById('snowfallCanvas')) { console.warn('Canvas already exists.'); return; } const container = document.querySelector('.santa-container'); if (!container) { console.error('Error: No element with class "santa-container" found.'); return; } canvas = document.createElement('canvas'); canvas.id = 'snowfallCanvas'; container.appendChild(canvas); ctx = canvas.getContext('2d'); resizeCanvas(container); window.addEventListener('resize', () => resizeCanvas(container)); } function removeCanvas() { const canvas = document.getElementById('snowfallCanvas'); if (canvas) { canvas.remove(); if (animationFrameId) { cancelAnimationFrame(animationFrameId); animationFrameId = null; console.log('Animation frame canceled'); } console.log('Canvas removed'); } } function resizeCanvas(container) { if (!canvas) return; const rect = container.getBoundingClientRect(); canvas.width = rect.width; canvas.height = rect.height; } function createSnowflakes(container) { return Array.from({ length: snowflakesCount }, () => ({ x: Math.random() * canvas.width, y: Math.random() * canvas.height, radius: Math.random() * 1.2 + 1, speed: Math.random() * snowFallSpeed + 1, swing: Math.random() * 2 - 1, })); } // Initialize snowflakes let snowflakes = []; function drawSnowflakes() { if (!ctx || !canvas) { console.error('Error: Canvas or context not found.'); return; } ctx.clearRect(0, 0, canvas.width, canvas.height); // empty canvas snowflakes.forEach(flake => { ctx.beginPath(); ctx.arc(flake.x, flake.y, flake.radius, 0, Math.PI * 2); ctx.fillStyle = 'white'; // color of snowflakes ctx.fill(); }); } function updateSnowflakes() { snowflakes.forEach(flake => { flake.y += flake.speed; // downwards movement flake.x += flake.swing; // sideways movement // reset snowflake if it reaches the bottom if (flake.y > canvas.height) { flake.y = 0; flake.x = Math.random() * canvas.width; // with new random X position } // wrap snowflakes around the screen edges if (flake.x > canvas.width) flake.x = 0; if (flake.x < 0) flake.x = canvas.width; }); } const presentImages = [ 'images/gift1.png', 'images/gift2.png', 'images/gift3.png', 'images/gift4.png', 'images/gift5.png', 'images/gift6.png', 'images/gift7.png', 'images/gift8.png', ]; const santaImage = 'images/santa.gif'; /* const presentImages = [ 'seasonals/santa_images/gift1.png', 'seasonals/santa_images/gift2.png', 'seasonals/santa_images/gift3.png', 'seasonals/santa_images/gift4.png', 'seasonals/santa_images/gift5.png', 'seasonals/santa_images/gift6.png', 'seasonals/santa_images/gift7.png', 'seasonals/santa_images/gift8.png', ]; const santaImage = 'seasonals/santa_images/santa.gif'; */ function createSantaElement() { const santa = document.createElement('img'); santa.src = santaImage; santa.classList.add('santa'); const santaContainer = document.querySelector('.santa-container'); santaContainer.appendChild(santa); return santa; } function dropPresent(santa) { const present = document.createElement('img'); present.src = presentImages[Math.floor(Math.random() * presentImages.length)]; present.classList.add('present'); const santaContainer = document.querySelector('.santa-container'); santaContainer.appendChild(present); // Position present at Santa's current location const rect = santa.getBoundingClientRect(); present.style.left = `${rect.left + rect.width / 2}px`; present.style.top = `${rect.top + rect.height}px`; // Calculate fall duration based on Santa's current height with variation const fallDistance = window.innerHeight - rect.top - rect.height; const baseDuration = fallDistance / 100; // 1 second per 100px const variation = Math.random() * 2000 - 1000; // ±1 second const fallDuration = baseDuration * 1000 + variation; // Animate present falling present.style.transition = `top ${fallDuration}ms linear, opacity ${fallDuration}ms linear`; present.style.top = `${window.innerHeight}px`; present.style.opacity = '0'; // Remove present after animation setTimeout(() => { present.remove(); }, fallDuration); } function animateSanta() { const santa = createSantaElement(); const santaContainer = document.querySelector('.santa-container'); const screenWidth = window.innerWidth; const screenHeight = window.innerHeight; const fromLeft = Math.random() < 0.5; const startX = fromLeft ? -100 : screenWidth + 100; const endX = fromLeft ? screenWidth + 100 : -100; const startY = Math.random() * (screenHeight / 2); const endY = Math.random() * (screenHeight / 2); const angle = Math.random() * 20 - 10; // -10 to 10 degrees santa.style.left = `${startX}px`; santa.style.top = `${startY}px`; santa.style.transform = `rotate(${angle}deg) ${fromLeft ? 'scaleX(-1)' : 'scaleX(1)'}`; // Mirror if not from left const duration = 10000; // 10 seconds const deltaX = endX - startX; const deltaY = endY - startY; const startTime = performance.now(); function move() { const currentTime = performance.now(); const elapsed = currentTime - startTime; const progress = Math.min(elapsed / duration, 1); santa.style.left = `${startX + deltaX * progress}px`; santa.style.top = `${startY + deltaY * progress - 100 * Math.sin(progress * Math.PI)}px`; // Parabolic path if (progress < 1) { requestAnimationFrame(move); } else { santa.remove(); const pause = Math.random() * 5000 + 3000; // 3-8 seconds pause setTimeout(animateSanta, pause); } } requestAnimationFrame(move); // Drop presents every 2 seconds const dropInterval = setInterval(() => dropPresent(santa), 2000); // Clear interval after animation completes setTimeout(() => { clearInterval(dropInterval); }, duration); } function animateAll() { drawSnowflakes(); updateSnowflakes(); animationFrameId = requestAnimationFrame(animateAll); } // initialize santa document.addEventListener('DOMContentLoaded', () => { if (!santaIsFlying) { console.warn('Sante is disabled.'); return; // exit if santa is disabled } const container = document.querySelector('.santa-container'); if (container) { const screenWidth = window.innerWidth; // get the screen width to detect mobile devices if (screenWidth < 768) { // lower count of snowflakes on mobile devices console.log('Mobile device detected. Reducing snowflakes count.'); snowflakesCount = snowflakesCountMobile; } console.log('Santa enabled.'); initializeCanvas(); snowflakes = createSnowflakes(container); animateAll(); animateSanta(); } });