1/** 2 * Sistema de renderizado de fases lunares usando fotos reales de NASA 3 * Versión 5.0 - Fotos Estáticas 4 */ 5 6class LunaRealista { 7 constructor() { 8 this.imagenesCargadas = {}; 9 this.cargando = new Set(); 10 } 11 12 /** 13 * Carga una imagen de fase lunar 14 */ 15 async cargarImagen(diaCiclo) { 16 // Si ya está cargada, retornarla 17 if (this.imagenesCargadas[diaCiclo]) { 18 return this.imagenesCargadas[diaCiclo]; 19 } 20 21 // Si ya se está cargando, esperar 22 if (this.cargando.has(diaCiclo)) { 23 return new Promise((resolve) => { 24 const checkInterval = setInterval(() => { 25 if (this.imagenesCargadas[diaCiclo]) { 26 clearInterval(checkInterval); 27 resolve(this.imagenesCargadas[diaCiclo]); 28 } 29 }, 50); 30 }); 31 } 32 33 // Marcar como cargando 34 this.cargando.add(diaCiclo); 35 36 return new Promise((resolve, reject) => { 37 const img = new Image(); 38 const numeroFase = String(diaCiclo).padStart(2, '0'); 39 40 img.onload = () => { 41 this.imagenesCargadas[diaCiclo] = img; 42 this.cargando.delete(diaCiclo); 43 resolve(img); 44 }; 45 46 img.onerror = () => { 47 this.cargando.delete(diaCiclo); 48 console.error(`â Error cargando fase_${numeroFase}.jpg`); 49 reject(new Error(`No se pudo cargar fase_${numeroFase}.jpg`)); 50 }; 51 52 img.src = `/static/images/fases/fase_${numeroFase}.jpg`; 53 }); 54 } 55 56 /** 57 * Crea un placeholder mientras carga la imagen 58 */ 59 crearPlaceholder(size) { 60 const canvas = document.createElement('canvas'); 61 canvas.width = size; 62 canvas.height = size; 63 const ctx = canvas.getContext('2d'); 64 65 // CÃrculo gris simple 66 ctx.fillStyle = '#555'; 67 ctx.beginPath(); 68 ctx.arc(size/2, size/2, size/2, 0, Math.PI * 2); 69 ctx.fill(); 70 71 return canvas; 72 } 73 74 /** 75 * Renderiza la Luna en un elemento del DOM 76 * 77 * @param {HTMLElement} elemento - Elemento donde renderizar 78 * @param {number} diaCiclo - DÃa del ciclo lunar (1-30) 79 * @param {number} size - Tamaño del canvas en pÃxeles 80 */ 81 async renderizar(elemento, diaCiclo, size = 200) { 82 try { 83 // Validar diaCiclo 84 if (!diaCiclo || diaCiclo < 1 || diaCiclo > 30) { 85 console.warn(`â ï¸ DÃa de ciclo inválido: ${diaCiclo}, usando 1`); 86 diaCiclo = 1; 87 } 88 89 // Mostrar placeholder mientras carga 90 const placeholder = this.crearPlaceholder(size); 91 elemento.innerHTML = ''; 92 placeholder.style.width = '100%'; 93 placeholder.style.height = 'auto'; 94 elemento.appendChild(placeholder); 95 96 // Cargar imagen real 97 const img = await this.cargarImagen(diaCiclo); 98 99 // Crear canvas final 100 const canvas = document.createElement('canvas'); 101 canvas.width = size; 102 canvas.height = size; 103 const ctx = canvas.getContext('2d'); 104 105 // ===== APLICAR MÃSCARA CIRCULAR (quita el fondo negro) ===== 106 // 1. Crear clip circular exacto al tamaño de la luna 107 // Medido: la luna en las fotos NASA ocupa el 87% del tamaño 108 ctx.save(); 109 ctx.beginPath(); 110 const radio = (size / 2) * 0.87; // Radio exacto de la luna 111 ctx.arc(size/2, size/2, radio, 0, Math.PI * 2); 112 ctx.clip(); 113 114 // 2. Dibujar imagen (solo se verá dentro del cÃrculo) 115 ctx.drawImage(img, 0, 0, size, size); 116 117 // 3. Restaurar contexto 118 ctx.restore(); 119 // ============================================================ 120 121 // Reemplazar placeholder con imagen real 122 elemento.innerHTML = ''; 123 canvas.style.width = '100%'; 124 canvas.style.height = 'auto'; 125 canvas.style.filter = 'drop-shadow(0 2px 10px rgba(212, 175, 55, 0.3))'; 126 elemento.appendChild(canvas); 127 128 } catch (error) { 129 console.error('Error renderizando luna:', error);
130 // Dejar el placeholder en caso de error 131 } 132 } 133 134 /** 135 * Precarga todas las imágenes (opcional, para mejor performance) 136 */ 137 async precargarTodas() { 138 console.log('ð¥ Precargando 30 fases lunares...'); 139 const promesas = []; 140 141 for (let dia = 1; dia <= 30; dia++) { 142 promesas.push(this.cargarImagen(dia)); 143 } 144 145 try { 146 await Promise.all(promesas); 147 console.log('â 30 fases lunares precargadas'); 148 } catch (error) { 149 console.error('â Error precargando fases:', error); 150 } 151 } 152} 153 154// Exportar instancia global 155window.lunaRealista = new LunaRealista(); 156 157// Precargar imágenes cuando el DOM esté listo 158if (document.readyState === 'loading') { 159 document.addEventListener('DOMContentLoaded', () => { 160 // Precargar en segundo plano (no bloquea la UI) 161 setTimeout(() => window.lunaRealista.precargarTodas(), 500); 162 }); 163} else { 164 setTimeout(() => window.lunaRealista.precargarTodas(), 500); 165}
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.