Meta Description" name="description" />
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Header Hero Pro - Polished</title>
<style>
body { margin: 0; overflow: hidden; background: #2c3e50; font-family: 'Segoe UI', sans-serif; touch-action: none; }
canvas { display: block; background: linear-gradient(#4facfe 0%, #00f2fe 100%); }
#ui { position: absolute; top: 30px; width: 100%; text-align: center; pointer-events: none; color: white; }
#score { font-size: 60px; font-weight: 900; text-shadow: 4px 4px 0px rgba(0,0,0,0.2); margin: 0; }
#highScore { font-size: 18px; opacity: 0.8; margin: 0; }
#combo-container { height: 30px; margin-top: 10px; }
#combo-text { color: #f1c40f; font-weight: bold; font-size: 24px; display: none; transform: scale(1.2); }
.overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; display: flex;
flex-direction: column; justify-content: center; align-items: center;
background: rgba(0,0,0,0.4); backdrop-filter: blur(5px); color: white; }
</style>
</head>
<body>
<div id="ui">
<p id="highScore">BEST: 0</p>
<h1 id="score">0</h1>
<div id="combo-container"><div id="combo-text">¡COMBO X5!</div></div>
</div>
<div id="start-screen" class="overlay">
<h1 style="font-size: 48px; margin-bottom: 10px;">HEADER HERO</h1>
<p>TOCA PARA EMPEZAR</p>
</div>
<canvas id="gameCanvas"></canvas>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreEl = document.getElementById('score');
const comboText = document.getElementById('combo-text');
const startScreen = document.getElementById('start-screen');
const highScoreEl = document.getElementById('highScore');
// --- Configuración y Estado ---
let gameState = 'START';
let score = 0, combo = 0, highScore = localStorage.getItem('headerHighScore') || 0;
let shake = 0;
highScoreEl.innerText = `BEST: ${highScore}`;
const physics = { gravity: 0.3, jump: -11, windIntensity: 0.04 };
const ball = { x: 0, y: 150, vx: 0, vy: 0, r: 22, color: '#fff', trail: [] };
const player = { x: 0, y: 0, targetX: 0, w: 70, h: 90, squash: 1 };
let particles = [];
let floatingTexts = [];
let currentWind = 0;
function init() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
ball.x = canvas.width / 2;
ball.y = 150;
ball.vx = 0; ball.vy = 0;
player.x = canvas.width / 2;
player.y = canvas.height - 150;
particles = [];
floatingTexts = [];
}
// --- Lógica de Partículas y Efectos ---
function spawnParticles(x, y, color, count = 8) {
for(let i=0; i<count; i++) {
particles.push({
x, y,
vx: (Math.random() - 0.5) * 10,
vy: (Math.random() - 0.5) * 10,
life: 1.0,
color
});
}
}
function addFloatingText(x, y, text, color) {
floatingTexts.push({ x, y, text, color, life: 1.0 });
}
// --- Bucle Principal ---
function update() {
if (gameState !== 'PLAYING') return;
// Viento y Gravedad
ball.vy += physics.gravity;
ball.vx += currentWind;
ball.vx *= 0.99; // Fricción
ball.x += ball.vx;
ball.y += ball.vy;
// Rebote paredes
if(ball.x < ball.r || ball.x > canvas.width - ball.r) ball.vx *= -0.8;
// Movimiento suave del jugador
player.x += (ball.x - player.x) * 0.2;
player.squash += (1 - player.squash) * 0.15;
// Partículas y Textos
particles.forEach(p => { p.x += p.vx; p.y += p.vy; p.life -= 0.02; });
particles = particles.filter(p => p.life > 0);
floatingTexts.forEach(t => { t.y -= 2; t.life -= 0.02; });
floatingTexts = floatingTexts.filter(t => t.life > 0);
if (shake > 0) shake *= 0.9;
if (ball.y > canvas.height) endGame();
}
function draw() {
ctx.save();
if (shake > 0) ctx.translate(Math.random()*shake - shake/2, Math.random()*shake - shake/2);
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Suelo
ctx.fillStyle = '#27ae60';
ctx.fillRect(0, canvas.height - 80, canvas.width, 80);
// Partículas
particles.forEach(p => {
ctx.globalAlpha = p.life;
ctx.fillStyle = p.color;
ctx.beginPath(); ctx.arc(p.x, p.y, 4, 0, Math.PI*2); ctx.fill();
});
ctx.globalAlpha = 1;
// Jugador (Cabeza con Squash)
ctx.fillStyle = '#f3e5ab';
ctx.beginPath();
ctx.ellipse(player.x, player.y - 30, 40, 40 * player.squash, 0, 0, Math.PI*2);
ctx.fill();
ctx.fillStyle = '#e67e22'; // Pelo/Gorra
ctx.arc(player.x, player.y - 45, 42, Math.PI, 0); ctx.fill();
// Pelota
ctx.shadowBlur = (combo >= 5) ? 20 : 0;
ctx.shadowColor = "yellow";
ctx.fillStyle = (combo >= 5) ? "#f1c40f" : "white";
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.r, 0, Math.PI*2);
ctx.fill();
ctx.strokeStyle = "#000"; ctx.lineWidth = 2; ctx.stroke();
ctx.shadowBlur = 0;
// Textos flotantes
floatingTexts.forEach(t => {
ctx.globalAlpha = t.life;
ctx.fillStyle = t.color;
ctx.font = "bold 24px Arial";
ctx.textAlign = "center";
ctx.fillText(t.text, t.x, t.y);
});
ctx.globalAlpha = 1;
ctx.restore();
requestAnimationFrame(() => { update(); draw(); });
}
// --- Interacción ---
function handleInput() {
if (gameState !== 'PLAYING') {
resetGame();
return;
}
const distY = Math.abs(ball.y - (player.y - 60));
const distX = Math.abs(ball.x - player.x);
if (distY < 60 && distX < 55 && ball.vy > 0) {
player.squash = 0.6; // Efecto de aplastamiento
ball.vy = physics.jump;
ball.vx += (ball.x - player.x) * 0.2; // Dirección basada en impacto
if (distY < 25) { // ¡PERFECTO!
score += 20; combo++; shake = 10;
spawnParticles(ball.x, ball.y, '#f1c40f', 15);
addFloatingText(ball.x, ball.y - 40, "PERFECTO!", "#f1c40f");
} else { // BUENO
score += 10; combo = 0; shake = 4;
spawnParticles(ball.x, ball.y, '#fff', 5);
addFloatingText(ball.x, ball.y - 40, "BIEN", "#fff");
}
currentWind = (Math.random() - 0.5) * physics.windIntensity * (score/50 + 1);
scoreEl.innerText = score;
updateComboUI();
}
}
function updateComboUI() {
if (combo >= 5) {
comboText.style.display = 'block';
comboText.innerText = `FUEGO x${Math.floor(combo/5) + 1}`;
} else {
comboText.style.display = 'none';
}
}
function endGame() {
gameState = 'GAMEOVER';
if (score > highScore) {
highScore = score;
localStorage.setItem('headerHighScore', highScore);
}
startScreen.style.display = 'flex';
startScreen.innerHTML = `<h1>PUNTUACIÓN: ${score}</h1><p>NUEVO RÉCORD: ${highScore}</p><p>TOCA PARA REINTENTAR</p>`;
}
function resetGame() {
score = 0; combo = 0;
scoreEl.innerText = "0";
highScoreEl.innerText = `BEST: ${highScore}`;
startScreen.style.display = 'none';
init();
gameState = 'PLAYING';
}
window.addEventListener('mousedown', handleInput);
window.addEventListener('touchstart', (e) => { e.preventDefault(); handleInput(); });
window.addEventListener('resize', init);
init(); draw();
</script>
</body>
</html>
1
1
8KB
8KB
215.0ms
608.0ms
215.0ms