Efecto de lluvia de meteoros con comentarios dinámicos y música de fondo

Introducción

Hace unos días, mientras escuchaba una canción antigua de mi juventud, se me ocurrió implementar este diseño web que combina tres efectos visuales interessantes: un cielo nocturno con lluvia de meteoros, comentarios en pantalla tipo弹幕y música de fondo. A continuación explico cómo implementar cada componente.

Resultado final

Primer paso: Fondo de lluvia de meteoros

1.1 Configuración del cielo nocturno

html,
body {
  width: 100%;
  height: 100%;
  margin: 0;
  overflow: hidden;
  background: radial-gradient(ellipse at bottom, #1b2735 0%, #090a0f 100%)
}

1.2 Estructura HTML

<div id="meteor-container">
  <div class="meteor" style="top: 0px;left: 500px;"></div>
</div>

1.3 Estilos CSS para los meteoros

#meteor-container {
  margin: 0 auto;
  width: 100vw;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
}

.meteor {
  display: block;
  width: 1px;
  background: transparent;
  position: relative;
  opacity: 0;
  filter: drop-shadow(0 0 6px #ffffff);
  animation: meteor-fall 3.5s linear infinite;
  -webkit-animation: meteor-fall 3.5s linear infinite;
  -moz-animation: meteor-fall 3.5s linear infinite;
}

.meteor::after {
  content: '';
  display: block;
  border: 0px solid #fff;
  border-width: 0px 90px 2px 90px;
  border-top-right-radius: 100%;
  border-bottom-right-radius: 100%;
  border-color: transparent transparent transparent rgba(255, 255, 255, .6);
  box-shadow: 0 0 1px 0 rgba(255, 255, 255, .1);
  transform: rotate(-45deg) translate3d(1px, 3px, 0);
  -webkit-transform: rotate(-45deg) translate3d(1px, 3px, 0);
  -moz-transform: rotate(-45deg) translate3d(1px, 3px, 0);
  transform-origin: 0% 100%;
  -webkit-transform-origin: 0% 100%;
  -moz-transform-origin: 0% 100%;
  
  animation: meteor-tail 3.5s linear initial;
  -webkit-animation: meteor-tail 3.5s linear initial;
  -moz-animation: meteor-tail 3.5s linear initial;
}

@keyframes meteor-fall {
  0% {
    opacity: 0;
    transform: scale(0.5) translate3d(0, 0, 0);
    -webkit-transform: scale(0.5) translate3d(0, 0, 0);
    -moz-transform: scale(0.5) translate3d(0, 0, 0);
  }
  50% {
    opacity: 1;
    transform: translate3d(-300px, 300px, 0);
    -webkit-transform: translate3d(-300px, 300px, 0);
    -moz-transform: translate3d(-300px, 300px, 0);
  }
  100% {
    opacity: 0;
    transform: scale(1.2) translate3d(-500px, 500px, 0);
    -webkit-transform: scale(1.2) translate3d(-500px, 500px, 0);
    -moz-transform: scale(1.2) translate3d(-500px, 500px, 0);
  }
}

@keyframes meteor-tail {
  0% {
    border-width: 0px 80px 2px 80px;
  }
  50% {
    border-width: 0px 90px 2px 90px;
  }
  100% {
    border-width: 0px 45px 2px 45px;
  }
}

1.4 Generación dinámica de meteoros con JavaScript

const container = document.getElementById("meteor-container");

function generarNumeroAleatorio(maximo, minimo) {
  return Math.floor(Math.random() * (maximo - minimo + 1) + minimo);
}

// Generar meteoros dinámicamente
for (let i = 0; i < 30; i++) {
  const nuevoMeteor = document.createElement("div");
  nuevoMeteor.className = "meteor";
  nuevoMeteor.style.top = generarNumeroAleatorio(300, -100) + "px";
  nuevoMeteor.style.left = generarNumeroAleatorio(1600, 300) + "px";
  container.appendChild(nuevoMeteor);
}

const meteoros = document.getElementsByClassName("meteor");

// Aplicar retrasos de animación aleatorios
for (let j = 0; j < meteoros.length; j++) {
  meteoros[j].style.animationDelay = j % 6 === 0 ? "0s" : j * 0.8 + "s";
}

Segundo paso: Sistema de comantarios en pantalla

2.1 Estructura HTML

<div class="danmaku-container"></div>

2.2 Estilos CSS

.danmaku-container {
  width: 100%;
  height: 100%;
  position: relative;
  z-index: 10;
}

@keyframes scroll-left {
  from {
    left: 100%;
    transform: translateX(0);
  }
  to {
    left: 0;
    transform: translateX(-100%);
  }
}

.comment-item {
  position: absolute;
  top: 50%;
  left: 100%;
  width: 100%;
  color: #fff;
}

2.3 Lógica JavaScript para mostrar comentarios

const mensajes = [
  "¡Aterrizaje exitoso!",
  "Advertencia de tentación",
  "Consejo: sube el volumen o usa auriculares",
  "¡Escudo de comentarios activado!",
  "Aunque talvez no nos volvamos a ver, solo quería decirte que me gustas",
  "¡Contenido de alto impacto!",
  "Nunca había visto a alguien tan desvergonzado",
  "¡Celebración de final!",
  "¡Aterrizaje exitoso!",
  "Advertencia de tentación",
  "Consejo: sube el volumen o usa auriculares"
];

const contenedor = document.querySelector(".danmaku-container");

function obtenerPosicionAleatoria() {
  return Math.floor(Math.random() * (700 + 50) - 50);
}

function obtenerDuracionAleatoria() {
  return Math.floor(Math.random() * 50);
}

function obtenerRetrasoAleatorio() {
  return Math.floor(Math.random() * 60);
}

mensajes.forEach(texto => {
  const elemento = document.createElement("div");
  elemento.classList.add("comment-item");
  elemento.style.top = obtenerPosicionAleatoria() + "px";
  elemento.style.animation = `scroll-left ${obtenerDuracionAleatoria()}s linear ${obtenerRetrasoAleatorio()}s`;
  elemento.textContent = texto;
  contenedor.appendChild(elemento);
});

Tercer paso: Música de fondo, imagen de fondo e interacción con clics

3.1 Reproducción de música

<audio src="musica/fondo.mp3" autoplay loop></audio>

3.2 Imagen de fondo

#meteor-container {
  margin: 0 auto;
  width: 100vw;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
  background-image: url("https://imagen-ejemplo.com/galaxia.jpg");
  background-size: contain;
  background-repeat: no-repeat;
  background-position: 50% 50%;
}

3.3 Efecto de estrelllas al hacer clic

Estilos CSS

.click-star {
  position: fixed;
  display: block;
  color: #fff;
  width: 0px;
  height: 0px;
  border-right: 10px solid transparent;
  border-bottom: 7px solid #fff;
  border-left: 10px solid transparent;
  transform: rotate(35deg);
  filter: drop-shadow(0 0 6px #ffffff);
}

.click-star::before {
  border-bottom: 8px solid #fff;
  border-left: 3px solid transparent;
  border-right: 3px solid transparent;
  position: absolute;
  height: 0;
  width: 0;
  top: -4.5px;
  left: -6.5px;
  display: block;
  content: '';
  transform: rotate(-35deg);
}

.click-star::after {
  position: absolute;
  display: block;
  color: #fff;
  top: .3px;
  left: -10.5px;
  width: 0px;
  height: 0px;
  border-right: 10px solid transparent;
  border-bottom: 7px solid #fff;
  border-left: 10px solid transparent;
  transform: rotate(-70deg);
  content: '';
}

Lógica JavaScript

const estrellasCreadas = [];
let animacionActiva = true;

iniciarBucleAnimacion();

document.addEventListener('click', (evento) => {
  generarEstrella(evento);
});

function generarEstrella(evento) {
  const elemento = document.createElement("span");
  elemento.className = "click-star";
  estrellasCreadas.push({
    elemento: elemento,
    posicionX: evento.clientX - 5,
    posicionY: evento.clientY - 5,
    escala: 1,
    transparencia: 1
  });
  document.body.appendChild(elemento);
}

function iniciarBucleAnimacion() {
  for (let i = 0; i < estrellasCreadas.length; i++) {
    if (estrellasCreadas[i].transparencia <= 0) {
      document.body.removeChild(estrellasCreadas[i].elemento);
      estrellasCreadas.splice(i, 1);
      continue;
    }
    
    estrellasCreadas[i].escala += 0.04;
    estrellasCreadas[i].transparencia -= 0.013;
    estrellasCreadas[i].elemento.style.cssText =
      "left:" + estrellasCreadas[i].posicionX + "px;" +
      "top:" + estrellasCreadas[i].posicionY + "px;" +
      "opacity:" + estrellasCreadas[i].transparencia + ";" +
      "transform:scale(" + estrellasCreadas[i].escala + "," + estrellasCreadas[i].escala + ");";
  }
  requestAnimationFrame(iniciarBucleAnimacion);
}

Publicado el 6-16 03:07