1(function () { 2 function ready(f) { 3 if (document.readyState !== 'loading') f(); 4 else document.addEventListener('DOMContentLoaded', f); 5 } 6 7 ready(function () { 8 const nebulaCanvas = document.getElementById('nebula-canvas'); 9 const starCanvas = document.getElementById('star-canvas'); 10 if (!nebulaCanvas || !starCanvas) return; 11 12 const nebCtx = nebulaCanvas.getContext('2d', { alpha: true, desynchronized: true }); 13 const starCtx = starCanvas.getContext('2d', { alpha: true, desynchronized: true }); 14 15 const DPR = Math.min(window.devicePixelRatio || 1, 2); 16 17 // Cache window.innerWidth/innerHeight to avoid layout calls 18 let W = window.innerWidth; 19 let H = window.innerHeight; 20 21 function resizeCanvas() { 22 W = window.innerWidth; 23 H = window.innerHeight; 24 25 [nebulaCanvas, starCanvas].forEach((c) => { 26 c.width = Math.floor(W * DPR); 27 c.height = Math.floor(H * DPR); 28 c.style.width = W + 'px'; 29 c.style.height = H + 'px'; 30 31 const ctx = c.getContext('2d'); 32 ctx.setTransform(1, 0, 0, 1, 0, 0); 33 ctx.scale(DPR, DPR); 34 }); 35 } 36 37 // IMPORTANT: run immediately (not only on window.load) 38 resizeCanvas(); 39 40 window.addEventListener('load', resizeCanvas); 41 window.addEventListener('orientationchange', resizeCanvas); 42 window.addEventListener('resize', () => { 43 clearTimeout(window._resizeTimeout); 44 window._resizeTimeout = setTimeout(() => { 45 resizeCanvas(); 46 // regen without changing visuals 47 createNebula(); 48 createStars(); 49 }, 120); 50 }); 51 52 // Cache for hexârgb conversion (massive performance win) 53 const rgbCache = {}; 54 function hexToRgb(hex) { 55 if (rgbCache[hex]) return rgbCache[hex]; 56 const h = hex.replace('#', ''); 57 const bigint = parseInt(h, 16); 58 const out = `${(bigint >> 16) & 255},${(bigint >> 8) & 255},${bigint & 255}`; 59 rgbCache[hex] = out; 60 return out; 61 } 62 63 // ----------------- Nebula ----------------- 64 let nebulaBlobs = []; 65 const nebulaColors = [ 66 'rgba(255,200,200,0.05)', 67 'rgba(180,150,255,0.05)', 68 'rgba(120,180,255,0.04)', 69 'rgba(255,150,100,0.04)', 70 ]; 71 72 function createNebula() { 73 nebulaBlobs.length = 0; 74 for (let i = 0; i < 10; i++) { 75 nebulaBlobs.push({ 76 x: Math.random() * W, 77 y: Math.random() * H, 78 radius: 200 + Math.random() * 300, 79 color: nebulaColors[(Math.random() * nebulaColors.length) | 0], 80 dx: (Math.random() - 0.5) * 0.05, 81 dy: (Math.random() - 0.5) * 0.05, 82 _gradient: null, 83 }); 84 } 85 } 86 87 function drawNebula() { 88 nebCtx.clearRect(0, 0, W, H); 89 90 for (let i = 0; i < nebulaBlobs.length; i++) { 91 const blob = nebulaBlobs[i]; 92 93 // Recompute gradient if missing (or after resize/regeneration) 94 if (!blob._gradient) { 95 const g = nebCtx.createRadialGradient(blob.x, blob.y, 0, blob.x, blob.y, blob.radius); 96 g.addColorStop(0, blob.color); 97 g.addColorStop(1, 'rgba(0,0,0,0)'); 98 blob._gradient = g; 99 } 100 101 nebCtx.fillStyle = blob._gradient; 102 nebCtx.beginPath(); 103 nebCtx.arc(blob.x, blob.y, blob.radius, 0, Math.PI * 2); 104 nebCtx.fill(); 105 106 blob.x += blob.dx; 107 blob.y += blob.dy; 108 109 if (blob.x < -blob.radius) blob.x = W + blob.radius; 110 else if (blob.x > W + blob.radius) blob.x = -blob.radius; 111 112 if (blob.y < -blob.radius) blob.y = H + blob.radius; 113 else if (blob.y > H + blob.radius) blob.y = -blob.radius; 114 } 115 } 116 117 // ----------------- Starfield ----------------- 118 let layers = []; 119 const TOTAL_LAYERS = 4; 120 const TOTAL_STARS = 6000; 121 122 function createStars() { 123 layers.length = 0; 124 125 for (let l = 0; l < TOTAL_LAYERS; l++) { 126 const layerStars = []; 127 const depthFactor = 1 + l; 128 const layerCount = TOTAL_STARS / TOTAL_LAYERS; 129 130 for (let i = 0; i < layerCount; i++) { 131 const clusterOffsetX = Math.random() < 0.02 ? (Math.random() - 0.5) * 50 : 0; 132 const clusterOffsetY = Math.random() < 0.02 ? (Math.random() - 0.5) * 50 : 0; 133 134 let baseColor = 135 l === 0 ? '#ffffff' : 136 l === 1 ? '#ffeedd' : 137 l === 2 ? '#fff8e0' : '#e0f0ff'; 138 139 if (Math.random() < 0.01) baseColor = '#ff6666'; 140 141 layerStars.push({ 142 x: Math.random() * W + clusterOffsetX, 143 y: Math.random() * H + clusterOffsetY, 144 radius: (Math.random() ** 2.5) * 1.3 + 0.1, 145 color: baseColor, 146 rgb: hexToRgb(baseColor), 147 baseOpacity: Math.random() * 0.35 + 0.15, 148 twinkleSpeed: Math.random() * 0.6 + 0.1 - l * 0.05, 149 phase: Math.random() * Math.PI * 2, 150 depth: depthFactor, 151 burstPhase: -Math.random() * 5, 152 specialBurst: false, 153 burstDuration: 0, 154 nextTwinkle: Math.random() * 5 + 1, 155 }); 156 } 157 layers.push(layerStars); 158 } 159 } 160 161 const shootingStars = []; 162 163 function createShootingStar() { 164 shootingStars.push({ 165 x: Math.random() * W, 166 y: Math.random() * H * 0.5, 167 length: 80 + Math.random() * 40,
168 speed: 800 + Math.random() * 400, 169 angle: Math.random() * Math.PI / 6 + Math.PI / 12, 170 life: 0, 171 }); 172 } 173 174 function drawStars(scrollOffset, t) { 175 starCtx.clearRect(0, 0, W, H); 176 177 for (let li = 0; li < layers.length; li++) { 178 const layer = layers[li]; 179 180 for (let si = 0; si < layer.length; si++) { 181 const s = layer[si]; 182 183 // Fast modulo 184 let y = s.y + scrollOffset / (s.depth * 10); 185 if (y >= H) y -= H; 186 else if (y < 0) y += H; 187 188 let opacity = s.baseOpacity + Math.sin(t * s.twinkleSpeed + s.phase) * 0.06; 189 190 if (!s.specialBurst && t > s.nextTwinkle) { 191 s.specialBurst = true; 192 s.burstPhase = t; 193 s.burstDuration = 0.5 + Math.random() * 0.5; 194 s.nextTwinkle = t + 3 + Math.random() * 5; 195 } 196 197 if (s.specialBurst) { 198 const dt = t - s.burstPhase; 199 if (dt < s.burstDuration) { 200 opacity += Math.sin((dt / s.burstDuration) * Math.PI) * 0.15; 201 } else { 202 s.specialBurst = false; 203 } 204 } 205 206 const shimmer = Math.sin((t - s.burstPhase) * 3) * Math.exp(-(t - s.burstPhase) * 1.2); 207 opacity += shimmer * 0.05; 208 209 const o = opacity < 0.1 ? 0.1 : opacity > 0.55 ? 0.55 : opacity; 210 211 starCtx.beginPath(); 212 starCtx.arc(s.x, y, s.radius, 0, Math.PI * 2); 213 starCtx.fillStyle = `rgba(${s.rgb},${o})`; 214 starCtx.fill(); 215 } 216 } 217 218 if (Math.random() < 0.002) createShootingStar(); 219 220 for (let i = shootingStars.length - 1; i >= 0; i--) { 221 const st = shootingStars[i]; 222 st.life += 0.016; 223 224 const dx = Math.cos(st.angle) * st.speed * 0.016; 225 const dy = Math.sin(st.angle) * st.speed * 0.016; 226 227 st.x += dx; 228 st.y += dy; 229 230 starCtx.beginPath(); 231 starCtx.moveTo(st.x, st.y); 232 starCtx.lineTo(st.x - dx * 3, st.y - dy * 3); 233 starCtx.strokeStyle = 'rgba(255,255,255,0.8)'; 234 starCtx.lineWidth = 1.5; 235 starCtx.stroke(); 236 237 if (st.x > W || st.y > H) shootingStars.splice(i, 1); 238 } 239 } 240 241 function animate(time) { 242 const scrollOffset = window.scrollY || 0; 243 drawNebula(); 244 drawStars(scrollOffset, time * 0.001); 245 requestAnimationFrame(animate); 246 } 247 248 createNebula(); 249 createStars(); 250 requestAnimationFrame(animate); 251 252 // WP Dark Mode: when toggled ON, force a resize + regenerate 253 new MutationObserver(() => { 254 if (document.documentElement.classList.contains('wp-dark-mode-active')) { 255 resizeCanvas(); 256 createNebula(); 257 createStars(); 258 } 259 }).observe(document.documentElement, { attributes: true, attributeFilter: ['class'] }); 260 }); 261})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.