(function() { 'use strict'; const isMobile = window.innerWidth <= 767; const isLowPower = navigator.hardwareConcurrency <= 4; if (isMobile || isLowPower) { document.addEventListener('DOMContentLoaded', () => { const canvas = document.getElementById('filter'); if (canvas) canvas.style.display = 'none'; }); return; } document.addEventListener('DOMContentLoaded', () => { const canvas = document.getElementById('filter'); if (!canvas) return; const ctx = canvas.getContext('2d'); const BASE_WIDTH = 1280; const BASE_HEIGHT = 720; const fps = 24; const frameDuration = 1000 / fps; const grainAlpha = 32; let lastTime = 0; let isVisible = false; canvas.width = BASE_WIDTH; canvas.height = BASE_HEIGHT; canvas.style.width = '100%'; canvas.style.height = '100%'; const grainTexture = ctx.createImageData(BASE_WIDTH, BASE_HEIGHT); const pixels = grainTexture.data; for (let i = 0; i < pixels.length; i += 4) { const gray = (Math.random() * 255) | 0; pixels[i] = pixels[i + 1] = pixels[i + 2] = gray; pixels[i + 3] = grainAlpha; } const observer = new IntersectionObserver((entries) => { isVisible = entries[0].isIntersecting; }, { threshold: 0 }); observer.observe(canvas); let offsetX = 0; let offsetY = 0; const draw = time => { requestAnimationFrame(draw); if (!isVisible) return; if (time - lastTime < frameDuration) return; lastTime = time; ctx.putImageData(grainTexture, offsetX, offsetY); if (Math.random() > 0.7) { offsetX = (offsetX + Math.floor(Math.random() * 3) - 1) % BASE_WIDTH; offsetY = (offsetY + Math.floor(Math.random() * 3) - 1) % BASE_HEIGHT; } }; if ('requestIdleCallback' in window) { requestIdleCallback(() => requestAnimationFrame(draw), { timeout: 500 }); } else { setTimeout(() => requestAnimationFrame(draw), 100); } }); })();