Cómo crear una Luna Animada con CSS y JavaScript (Paso a Paso)

- Andrés Cruz

EN In english

Cómo crear una Luna Animada con CSS y JavaScript (Paso a Paso)
En este tutorial aprenderás a crear una Luna animada utilizando solo CSS para el diseño y las animaciones, y un toque de JavaScript para generar un fondo estrellado dinámico. Exploraremos el uso de box-shadow (tanto exterior como interior) para lograr efectos de relieve y profundidad de forma sencilla pero efectiva.

El diseño web moderno no solo se trata de estructuras estáticas; las pequeñas animaciones y detalles visuales, como este experimento de una luna con cráteres y fases, añaden una capa de interacción que mejora la experiencia del usuario. A continuación, veremos cómo lograr este efecto paso a paso.

Construyendo nuestra Luna en tres pasos

1. La estructura base y el halo lunar

Todo comienza con un simple div circular. Para darle ese aspecto "brillante", utilizaremos la propiedad box-shadow. En lugar de un color sólido, aplicaremos una animación de pulsación que emula el resplandor natural de la luna en una noche despejada.

He modernizado el posicionamiento utilizando left: 50% y transform para asegurar que la luna esté siempre centrada independientemente del tamaño del contenedor:

#moon {
    position: absolute;
    top: 15%;
    left: 50%;
    transform: translateX(-50%);
    width: 200px;
    height: 200px;
    border-radius: 50%;
    background: #CCC;
    box-shadow: 0 0 100px #FFFF8C;
    z-index: 5;
    animation: moonPulse 3s infinite ease-in-out;
}
@keyframes moonPulse {
    0%, 100% { box-shadow: 0 0 80px #FFF; }
    50% { box-shadow: 0 0 120px #FFF; }
}
 

2. Cráteres y profundidad con Box-Shadow Inset

Para los cráteres, el secreto está en la propiedad box-shadow: inset. Esto crea una sombra interna que da la sensación de hundimiento sin necesidad de imágenes o SVG complejos. Además, añadimos una capa superior para simular las fases lunares mediante sombras.

#moonFase {
    position: absolute;
    top: 15%;
    left: 50%;
    transform: translateX(-50%);
    width: 200px;
    height: 200px;
    border-radius: 50%;
    background: rgba(153, 153, 153, 0);
    /* Sombra interna para efecto de fase */
    box-shadow: inset -25px 0px 0 0px #999;
    z-index: 6;
    animation: moonFaseAnimation 600s infinite linear;
}
.crater {
    position: absolute;
    background: #555;
    box-shadow: inset 3px -2px 0 0px #414043;
    z-index: 7;
    border-radius: 50%;
}

Tip de SEO y Rendimiento: Al usar CSS puro en lugar de imágenes pesadas, reducimos el tiempo de carga de la página, un factor clave para el posicionamiento en Google.

3. Fondo estrellado dinámico con JavaScript Moderno

Posicionar 250 estrellas manualmente sería ineficiente. Por ello, utilizamos un pequeño script que genera las estrellas en posiciones aleatorias. He actualizado el código original a ES6 utilizando const y DocumentFragment para minimizar los "reflows" del navegador, mejorando el rendimiento.

const generateStars = () => {
    const w = window.innerWidth;
    const h = window.innerHeight;
    const fragment = document.createDocumentFragment();
    for (let i = 0; i < 250; i++) {
        const star = document.createElement("div");
        star.className = "star";
        
        // Posicionamiento aleatorio
        const x = Math.floor(Math.random() * w);
        const y = Math.floor(Math.random() * h);
        
        star.style.bottom = `${y}px`;
        star.style.right = `${x}px`;
        
        fragment.appendChild(star);
    }
    document.body.appendChild(fragment);
};
generateStars();

El estilo de las estrellas sigue siendo minimalista, pero con un ligero resplandor para que parezcan titilar:

.star {
    width: 2px;
    height: 2px;
    border-radius: 50%;
    background: #FFF;
    box-shadow: 0 0 2px 1px rgba(255, 255, 255, 0.8);
    position: absolute;
}

Llevándolo más allá: Personalización

Este experimento es la base perfecta para practicar. Puedes intentar cambiar el color de la luna por un tono rojizo para un "eclipse lunar", o modificar la velocidad de la animación de las fases. Si quieres profundizar en el mundo de las animaciones, te recomiendo leer mi artículo sobre cómo dar tus primeros pasos con animaciones en CSS.

¿Te ha gustado este tutorial? No olvides revisar también cómo resaltar contenido de manera animada con CSS para seguir mejorando tus habilidades de diseño web.

En esta entrada veremos cómo crear una Luna la cual estará compuesta por su halo, cráteres, fases y un cielo estrellado.

Acepto recibir anuncios de interes sobre este Blog.

Andrés Cruz

EN In english
<script> window.addEventListener('scroll', function() { if (window.scriptsLoaded) return; loadThirdPartyScripts(); }, { once: true }); window.addEventListener('mousemove', function() { if (window.scriptsLoaded) return; loadThirdPartyScripts(); }, { once: true }); window.addEventListener('touchstart', function() { if (window.scriptsLoaded) return; loadThirdPartyScripts(); }, { once: true }); // Fallback if no interaction window.addEventListener('load', function() { setTimeout(function() { if (!window.scriptsLoaded) loadThirdPartyScripts(); }, 8000); }); function loadThirdPartyScripts() { if (window.scriptsLoaded) return; window.scriptsLoaded = true; console.log('Loading third party scripts...'); // Google Analytics var gtagScript = document.createElement('script'); gtagScript.src = 'https://www.googletagmanager.com/gtag/js?id=G-F22688T9RL'; gtagScript.async = true; document.head.appendChild(gtagScript); gtagScript.onload = function() { window.dataLayer = window.dataLayer || []; function gtag() { dataLayer.push(arguments); } gtag('js', new Date()); gtag('config', 'G-F22688T9RL'); }; // Google ADS const adScript = document.createElement('script'); adScript.src = "https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"; adScript.setAttribute('data-ad-client', 'ca-pub-5280469223132298'); adScript.async = true; document.head.appendChild(adScript); // Facebook Pixel (function(f, b, e, v, n, t, s) { if (f.fbq) return; n = f.fbq = function() { n.callMethod ? n.callMethod.apply(n, arguments) : n.queue.push(arguments) }; if (!f._fbq) f._fbq = n; n.push = n; n.loaded = !0; n.version = '2.0'; n.queue = []; t = b.createElement(e); t.async = !0; t.src = v; s = b.getElementsByTagName(e)[0]; s.parentNode.insertBefore(t, s); })(window, document, 'script', 'https://connect.facebook.net/en_US/fbevents.js'); fbq('init', '1643487712945352'); fbq('track', 'PageView'); } </script> <noscript> <img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=1643487712945352&ev=PageView&noscript=1" /> </noscript>