// SNOWARE — Dynamic Nature Background (Stars + Mountains + Meteors) (function(){ var canvas = document.createElement('canvas'); canvas.id = 'nature-bg'; canvas.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;z-index:-1;pointer-events:none;'; document.body.prepend(canvas); var ctx = canvas.getContext('2d'); var stars = [], shootingStars = []; var W, H; var lastShooting = 0; function resize(){ W = canvas.width = window.innerWidth; H = canvas.height = window.innerHeight; initStars(); } function initStars(){ stars = []; var count = Math.floor(W * H / 2800); for(var i = 0; i < count; i++){ stars.push({ x: Math.random() * W, y: Math.random() * H * 0.55, r: Math.random() * 1.5 + 0.3, a: Math.random() * 0.6 + 0.2, speed: Math.random() * 0.008 + 0.002, phase: Math.random() * Math.PI * 2 }); } } function addShootingStar(){ shootingStars.push({ x: Math.random() * W * 0.6 + W * 0.1, y: Math.random() * H * 0.25, len: Math.random() * 120 + 60, speed: Math.random() * 6 + 4, angle: Math.random() * 0.25 + 0.15, a: 1, life: 0, sparks: [] }); } function getTheme(){ return document.documentElement.getAttribute('data-theme') || 'warm'; } function getColors(){ var t = getTheme(); if(t === 'cyan'){ return { sky1: '#0a1525', sky2: '#101828', sky3: '#182436', star: '#c8dff0', starBright: '#81d4fa', mtn1: '#0e1826', mtn2: '#162230', mtn3: '#1e2e40', mtnLine: 'rgba(92,184,228,0.12)', mtnSnow: 'rgba(200,220,240,0.08)', shoot: '#81d4fa', shootGlow: 'rgba(129,212,250,0.2)' }; } return { sky1: '#0a0c08', sky2: '#141210', sky3: '#1a1710', star: '#d4c0a0', starBright: '#e8c9a0', mtn1: '#0e0c08', mtn2: '#181610', mtn3: '#242014', mtnLine: 'rgba(200,168,122,0.08)', mtnSnow: 'rgba(232,201,160,0.06)', shoot: '#e8c9a0', shootGlow: 'rgba(232,201,160,0.15)' }; } function drawSky(c){ var g = ctx.createLinearGradient(0, 0, 0, H); g.addColorStop(0, c.sky1); g.addColorStop(0.35, c.sky2); g.addColorStop(1, c.sky3); ctx.fillStyle = g; ctx.fillRect(0, 0, W, H); } function drawStars(c, t){ for(var i = 0; i < stars.length; i++){ var s = stars[i]; var twinkle = Math.sin(t * s.speed + s.phase) * 0.3 + 0.7; var alpha = s.a * twinkle; ctx.beginPath(); ctx.arc(s.x, s.y, s.r, 0, Math.PI * 2); ctx.fillStyle = s.r > 1.2 ? c.starBright : c.star; ctx.globalAlpha = alpha; ctx.fill(); } ctx.globalAlpha = 1; } function drawShootingStars(c, t){ // More frequent meteors: every 3-7 seconds if(t - lastShooting > 3000 + Math.random() * 4000){ addShootingStar(); lastShooting = t; } for(var i = shootingStars.length - 1; i >= 0; i--){ var s = shootingStars[i]; s.life += 1; s.x += s.speed; s.y += s.speed * s.angle; s.a -= 0.008; if(s.a <= 0){ shootingStars.splice(i, 1); continue; } // Head glow ctx.save(); ctx.globalAlpha = s.a * 0.6; var headGlow = ctx.createRadialGradient(s.x, s.y, 0, s.x, s.y, 8); headGlow.addColorStop(0, c.shoot); headGlow.addColorStop(1, 'transparent'); ctx.fillStyle = headGlow; ctx.beginPath(); ctx.arc(s.x, s.y, 8, 0, Math.PI * 2); ctx.fill(); // Main trail ctx.globalAlpha = s.a; ctx.strokeStyle = c.shoot; ctx.lineWidth = 1.8; ctx.lineCap = 'round'; ctx.beginPath(); ctx.moveTo(s.x, s.y); var tailX = s.x - s.len * Math.cos(s.angle); var tailY = s.y - s.len * Math.sin(s.angle); ctx.lineTo(tailX, tailY); ctx.stroke(); // Wide glow trail ctx.globalAlpha = s.a * 0.25; ctx.lineWidth = 6; ctx.strokeStyle = c.shoot; ctx.beginPath(); ctx.moveTo(s.x, s.y); ctx.lineTo(s.x - s.len * 0.5 * Math.cos(s.angle), s.y - s.len * 0.5 * Math.sin(s.angle)); ctx.stroke(); // Secondary thin glow trail ctx.globalAlpha = s.a * 0.12; ctx.lineWidth = 12; ctx.strokeStyle = c.shootGlow; ctx.beginPath(); ctx.moveTo(s.x, s.y); ctx.lineTo(s.x - s.len * 0.3 * Math.cos(s.angle), s.y - s.len * 0.3 * Math.sin(s.angle)); ctx.stroke(); ctx.restore(); } } function drawMountainLayer(yBase, height, color, lineColor, snowPatches, seed){ ctx.beginPath(); ctx.moveTo(0, yBase); var step = 3; for(var x = 0; x <= W; x += step){ var h = 0; h += Math.sin(x * 0.003 + seed) * height * 0.5; h += Math.sin(x * 0.007 + seed * 2) * height * 0.3; h += Math.sin(x * 0.015 + seed * 3) * height * 0.15; ctx.lineTo(x, yBase - Math.abs(h)); } ctx.lineTo(W, H); ctx.lineTo(0, H); ctx.closePath(); ctx.fillStyle = color; ctx.fill(); // subtle outline ctx.strokeStyle = lineColor; ctx.lineWidth = 1; ctx.stroke(); // snow patches on peaks if(snowPatches > 0){ ctx.globalAlpha = snowPatches; ctx.fillStyle = lineColor; for(var x = 0; x <= W; x += step){ var h = 0; h += Math.sin(x * 0.003 + seed) * height * 0.5; h += Math.sin(x * 0.007 + seed * 2) * height * 0.3; h += Math.sin(x * 0.015 + seed * 3) * height * 0.15; var peakY = yBase - Math.abs(h); if(Math.abs(h) > height * 0.6){ ctx.beginPath(); ctx.arc(x, peakY + 2, 2 + Math.random(), 0, Math.PI * 2); ctx.fill(); } } ctx.globalAlpha = 1; } } function drawMountains(c){ // far layer drawMountainLayer(H * 0.65, H * 0.18, c.mtn1, c.mtnLine, c.mtnSnow, 1.2); // mid layer drawMountainLayer(H * 0.72, H * 0.14, c.mtn2, c.mtnLine, 0, 3.7); // near layer drawMountainLayer(H * 0.82, H * 0.10, c.mtn3, c.mtnLine, 0, 7.1); } var startTime = performance.now(); function render(){ var t = (performance.now() - startTime) / 1000; var c = getColors(); ctx.clearRect(0, 0, W, H); drawSky(c); drawStars(c, t); drawShootingStars(c, performance.now()); drawMountains(c); requestAnimationFrame(render); } resize(); window.addEventListener('resize', resize); render(); })();