<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- MODIFICAR: El título que aparece en la pestaña del navegador -->
<title>¡Feliz Cumpleaños!</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<canvas id="canvas"></canvas>
<div class="stars" id="stars"></div>
<!-- MODIFICAR: El mensaje principal que se muestra en pantalla -->
<div class="message" id="message">¡FELIZ CUMPLEAÑOS!</div>
<!-- MODIFICAR: El texto que aparece en el botón -->
<button class="launch-button" onclick="launchFireworks()">🎆 ¡Más Fuegos Artificiales! 🎆</button>
</body>
<script src="script.js"></script>
</html>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
/* MODIFICAR: El degradado de fondo de toda la página. */
background: linear-gradient(135deg, #0c0c2e 0%, #1a1a3e 50%, #2d1b69 100%);
overflow: hidden;
font-family: 'Arial Black', Arial, sans-serif;
font-size: 16px;
}
canvas {
display: block;
/* MODIFICAR: Este es un brillo radial en la parte inferior del canvas. */
background: radial-gradient(circle at 50% 100%, rgba(20, 20, 60, 0.8) 0%, transparent 50%);
}
.message {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: transparent;
/* MODIFICAR: Tamaño de la fuente del mensaje principal. */
font-size: 4rem;
font-weight: bold;
text-align: center;
/* MODIFICAR: El degradado de colores para el texto. Añade o quita colores aquí. */
background: linear-gradient(45deg, #ff6b6b, #4ecdc4, #45b7d1, #96ceb4, #ffeaa7, #dda0dd);
background-size: 400% 400%;
-webkit-background-clip: text;
background-clip: text;
animation: rainbow 3s ease-in-out infinite, glow 2s ease-in-out infinite alternate;
opacity: 0;
z-index: 10;
width: 90%;
line-height: 1.2;
}
.message.show {
opacity: 1;
animation: rainbow 3s ease-in-out infinite, glow 2s ease-in-out infinite alternate, fadeIn 2s ease-in forwards;
}
@keyframes rainbow {
0%, 100% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
}
@keyframes glow {
/* MODIFICAR: El efecto de brillo del texto. Puedes cambiar los colores y la intensidad del desenfoque. */
from {
text-shadow: 0 0 20px rgba(255, 255, 255, 0.8), 0 0 40px rgba(255, 255, 255, 0.6);
}
to {
text-shadow: 0 0 30px rgba(255, 255, 255, 1), 0 0 60px rgba(255, 255, 255, 0.8);
}
}
@keyframes fadeIn {
from { opacity: 0; transform: translate(-50%, -50%) scale(0.8); }
to { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}
.stars {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
}
.star {
position: absolute;
/* MODIFICAR: Tamaño de las estrellas en el fondo. */
width: 2px;
height: 2px;
/* MODIFICAR: Color de las estrellas. */
background: white;
border-radius: 50%;
animation: twinkle 2s ease-in-out infinite alternate;
}
@keyframes twinkle {
from { opacity: 0.3; transform: scale(0.8); }
to { opacity: 1; transform: scale(1.2); }
}
.confetti {
position: absolute;
/* MODIFICAR: Tamaño de los trozos de confeti. */
width: 8px;
height: 8px;
background: #ff6b6b;
/* MODIFICAR: Duración de la caída del confeti (ej: 3s = 3 segundos). */
animation: confetti-fall 3s ease-in forwards;
pointer-events: none;
}
@keyframes confetti-fall {
0% { transform: translateY(-100vh) rotateZ(0deg); opacity: 1; }
100% { transform: translateY(100vh) rotateZ(720deg); opacity: 0; }
}
.launch-button {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
/* MODIFICAR: El degradado de color del botón. */
background: linear-gradient(45deg, #ff6b6b, #4ecdc4);
color: white;
border: none;
padding: 15px 30px;
font-size: 1.2rem;
font-weight: bold;
border-radius: 50px;
cursor: pointer;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
transition: all 0.3s ease;
z-index: 20;
white-space: nowrap;
}
.launch-button:hover {
transform: translateX(-50%) scale(1.05);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
}
/* Las reglas @media ajustan el diseño para diferentes tamaños de pantalla. */
@media (max-width: 1024px) {
.message { font-size: 3.5rem; }
.launch-button { padding: 12px 25px; font-size: 1.1rem; }
}
@media (max-width: 768px) {
.message { font-size: 2.8rem; }
.launch-button { padding: 10px 20px; font-size: 1rem; bottom: 15px; }
}
@media (max-width: 480px) {
.message { font-size: 2rem; padding: 0 10px; }
.launch-button { padding: 8px 15px; font-size: 0.9rem; bottom: 10px; }
.star { width: 1px; height: 1px; }
.confetti { width: 6px; height: 6px; }
}
@media (max-width: 320px) {
.message { font-size: 1.5rem; }
.launch-button { font-size: 0.8rem; padding: 6px 10px; }
}
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let fireworks = [];
let particles = [];
let messageShown = false;
// MODIFICAR: La paleta de colores para los fuegos artificiales y el confeti.
const colors = [
'#ff6b6b', '#4ecdc4', '#45b7d1', '#96ceb4', '#ffeaa7', '#dda0dd',
'#ff9ff3', '#54a0ff', '#5f27cd', '#00d2d3', '#ff9f43', '#ee5a24',
'#0abde3', '#10ac84', '#f368e0', '#feca57'
];
class Firework {
constructor(x, y, targetY, color) {
this.x = x;
this.y = y;
this.targetY = targetY;
this.color = color;
// MODIFICAR: Velocidad del cohete. '5' es la base, '* 3' es el rango aleatorio extra.
this.speed = Math.random() * 3 + 5;
this.trail = [];
this.exploded = false;
}
update() {
if (!this.exploded) {
this.trail.push({x: this.x, y: this.y});
// MODIFICAR: Longitud de la estela del cohete.
if (this.trail.length > 10) this.trail.shift();
this.y -= this.speed;
if (this.y <= this.targetY) {
this.explode();
this.exploded = true;
}
}
}
draw() {
if (!this.exploded) {
this.trail.forEach((point, index) => {
ctx.globalAlpha = index / this.trail.length;
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(point.x, point.y, 3, 0, Math.PI * 2);
ctx.fill();
});
ctx.globalAlpha = 1;
ctx.fillStyle = this.color;
ctx.shadowBlur = 20;
ctx.shadowColor = this.color;
ctx.beginPath();
ctx.arc(this.x, this.y, 5, 0, Math.PI * 2);
ctx.fill();
ctx.shadowBlur = 0;
}
}
explode() {
// MODIFICAR: Cantidad de partículas por explosión. '30' es la base, '* 50' es el rango.
const particleCount = Math.random() * 50 + 30;
for (let i = 0; i < particleCount; i++) {
particles.push(new Particle(this.x, this.y, this.color));
}
if (!messageShown && fireworks.filter(f => f.exploded).length > 2) {
document.getElementById('message').classList.add('show');
messageShown = true;
createConfetti();
}
}
}
class Particle {
constructor(x, y, color) {
this.x = x;
this.y = y;
this.color = color;
// MODIFICAR: La fuerza de la explosión. Un número más alto (ej: 15) hará que las partículas se dispersen más lejos.
const explosionStrength = 10;
this.vx = (Math.random() - 0.5) * explosionStrength;
this.vy = (Math.random() - 0.5) * explosionStrength;
this.life = 1;
// MODIFICAR: Duración de las partículas. Un valor más bajo (ej: 0.01) las hace durar más.
this.decay = Math.random() * 0.02 + 0.01;
// MODIFICAR: Tamaño de las partículas. '2' es la base, '* 4' es el rango aleatorio.
this.size = Math.random() * 4 + 2;
// MODIFICAR: La fuerza de gravedad que afecta a las partículas al caer.
this.gravity = 0.1;
}
update() {
this.x += this.vx;
this.y += this.vy;
this.vy += this.gravity;
this.life -= this.decay;
this.vx *= 0.99;
this.vy *= 0.99;
}
draw() {
ctx.globalAlpha = this.life;
ctx.fillStyle = this.color;
ctx.shadowBlur = 15;
ctx.shadowColor = this.color;
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.fill();
ctx.shadowBlur = 0;
}
}
function createFirework() {
const x = Math.random() * canvas.width;
const y = canvas.height;
const targetY = Math.random() * canvas.height * 0.4 + (canvas.height * 0.1);
const color = colors[Math.floor(Math.random() * colors.length)];
fireworks.push(new Firework(x, y, targetY, color));
}
function createStars() {
const starsContainer = document.getElementById('stars');
starsContainer.innerHTML = '';
// MODIFICAR: La cantidad de estrellas que aparecen en el fondo.
const starCount = 100;
for (let i = 0; i < starCount; i++) {
const star = document.createElement('div');
star.className = 'star';
star.style.left = Math.random() * 100 + '%';
star.style.top = Math.random() * 100 + '%';
star.style.animationDelay = Math.random() * 2 + 's';
starsContainer.appendChild(star);
}
}
function createConfetti() {
// MODIFICAR: La cantidad de trozos de confeti que caen.
const confettiCount = 50;
for (let i = 0; i < confettiCount; i++) {
setTimeout(() => {
const confetti = document.createElement('div');
confetti.className = 'confetti';
confetti.style.left = Math.random() * 100 + '%';
confetti.style.background = colors[Math.floor(Math.random() * colors.length)];
confetti.style.animationDelay = Math.random() * 3 + 's';
document.body.appendChild(confetti);
setTimeout(() => {
confetti.remove();
}, 3000);
}, i * 100);
}
}
function launchFireworks() {
// MODIFICAR: Cantidad de fuegos artificiales que se lanzan al presionar el botón.
const launchCount = 5;
for (let i = 0; i < launchCount; i++) {
setTimeout(() => createFirework(), i * 200);
}
}
function animate() {
// MODIFICAR: El efecto de estela. Un valor más bajo (ej: 0.05) deja estelas más largas.
ctx.fillStyle = 'rgba(12, 12, 46, 0.1)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
fireworks.forEach((firework, index) => {
firework.update();
firework.draw();
if (firework.exploded && firework.trail.length === 0) {
fireworks.splice(index, 1);
}
});
particles.forEach((particle, index) => {
particle.update();
particle.draw();
if (particle.life <= 0) {
particles.splice(index, 1);
}
});
requestAnimationFrame(animate);
}
createStars();
// MODIFICAR: Frecuencia de los fuegos artificiales automáticos (en milisegundos. 800 = 0.8 segundos).
setInterval(createFirework, 800);
animate();
window.addEventListener('resize', () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
createStars();
});
// Lanzamiento inicial
setTimeout(() => {
// MODIFICAR: Cantidad de fuegos artificiales en la ráfaga inicial.
const initialBurst = 3;
for (let i = 0; i < initialBurst; i++) {
setTimeout(() => createFirework(), i * 300);
}
}, 1000);