aiTEST1 / index.html
AptlyDigital's picture
Update index.html
29d028b verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bubble Pop Frenzy - Touch Screen Game</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
touch-action: manipulation;
user-select: none;
}
body {
font-family: 'Arial Rounded MT Bold', 'Arial', sans-serif;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
color: white;
overflow: hidden;
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.container {
width: 100%;
max-width: 800px;
height: 90vh;
display: flex;
flex-direction: column;
align-items: center;
padding: 15px;
}
.header {
text-align: center;
margin-bottom: 15px;
width: 100%;
}
h1 {
font-size: 2.8rem;
margin-bottom: 5px;
background: linear-gradient(90deg, #ff9a9e, #fad0c4, #fad0c4, #a18cd1);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
text-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}
.subtitle {
font-size: 1.2rem;
color: #a1c4fd;
margin-bottom: 10px;
}
.game-info {
display: flex;
justify-content: space-between;
width: 100%;
background: rgba(0, 0, 0, 0.3);
border-radius: 15px;
padding: 15px;
margin-bottom: 15px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
border: 2px solid rgba(255, 255, 255, 0.1);
}
.info-box {
text-align: center;
flex: 1;
}
.info-title {
font-size: 0.9rem;
color: #a1c4fd;
margin-bottom: 5px;
}
.info-value {
font-size: 2.2rem;
font-weight: bold;
color: #ffd166;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.game-container {
position: relative;
width: 100%;
flex-grow: 1;
background: rgba(10, 15, 35, 0.7);
border-radius: 20px;
overflow: hidden;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
border: 3px solid rgba(255, 255, 255, 0.1);
margin-bottom: 15px;
}
#game-canvas {
width: 100%;
height: 100%;
display: block;
}
.controls {
display: flex;
justify-content: center;
gap: 20px;
width: 100%;
margin-top: 10px;
}
.btn {
padding: 15px 30px;
font-size: 1.2rem;
border: none;
border-radius: 50px;
background: linear-gradient(90deg, #ff9a9e, #fad0c4);
color: #333;
font-weight: bold;
cursor: pointer;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.btn:active {
transform: scale(0.95);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}
.btn-restart {
background: linear-gradient(90deg, #a18cd1, #fbc2eb);
}
.btn-pause {
background: linear-gradient(90deg, #ffd166, #ffef9f);
}
.instructions {
background: rgba(0, 0, 0, 0.3);
border-radius: 15px;
padding: 15px;
margin-top: 15px;
width: 100%;
text-align: center;
font-size: 1rem;
line-height: 1.5;
color: #a1c4fd;
border: 2px solid rgba(255, 255, 255, 0.1);
}
.instructions strong {
color: #ffd166;
}
.game-over {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.85);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 10;
border-radius: 20px;
display: none;
}
.game-over h2 {
font-size: 3.5rem;
color: #ff9a9e;
margin-bottom: 20px;
text-shadow: 0 3px 6px rgba(0, 0, 0, 0.5);
}
.final-score {
font-size: 2.5rem;
color: #ffd166;
margin-bottom: 30px;
}
.bubble-info {
display: flex;
justify-content: center;
gap: 15px;
margin-top: 15px;
flex-wrap: wrap;
}
.bubble-type {
display: flex;
align-items: center;
gap: 8px;
font-size: 0.9rem;
}
.bubble-demo {
width: 24px;
height: 24px;
border-radius: 50%;
}
.pulse {
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
@media (max-width: 768px) {
h1 {
font-size: 2.2rem;
}
.info-value {
font-size: 1.8rem;
}
.btn {
padding: 12px 20px;
font-size: 1.1rem;
}
.controls {
gap: 10px;
}
}
@media (max-width: 480px) {
.container {
padding: 10px;
}
h1 {
font-size: 1.8rem;
}
.subtitle {
font-size: 1rem;
}
.game-info {
padding: 10px;
}
.info-value {
font-size: 1.5rem;
}
.btn {
padding: 10px 15px;
font-size: 1rem;
}
.instructions {
font-size: 0.9rem;
padding: 10px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1><i class="fas fa-bubbles"></i> Bubble Pop Frenzy</h1>
<p class="subtitle">Touch the bubbles to pop them! Don't let them escape!</p>
</div>
<div class="game-info">
<div class="info-box">
<div class="info-title">SCORE</div>
<div id="score" class="info-value">0</div>
</div>
<div class="info-box">
<div class="info-title">TIME LEFT</div>
<div id="timer" class="info-value">60</div>
</div>
<div class="info-box">
<div class="info-title">BUBBLES</div>
<div id="bubbles-count" class="info-value">0</div>
</div>
</div>
<div class="game-container">
<canvas id="game-canvas"></canvas>
<div id="game-over" class="game-over">
<h2>GAME OVER</h2>
<div class="final-score">Score: <span id="final-score">0</span></div>
<button id="restart-btn" class="btn btn-restart pulse">
<i class="fas fa-redo"></i> PLAY AGAIN
</button>
</div>
</div>
<div class="bubble-info">
<div class="bubble-type">
<div class="bubble-demo" style="background: radial-gradient(circle at 30% 30%, #ff9a9e, #ff6b6b);"></div>
<span>Normal (+10)</span>
</div>
<div class="bubble-type">
<div class="bubble-demo" style="background: radial-gradient(circle at 30% 30%, #a18cd1, #6a5acd);"></div>
<span>Speed (+20)</span>
</div>
<div class="bubble-type">
<div class="bubble-demo" style="background: radial-gradient(circle at 30% 30%, #ffd166, #ff9e00);"></div>
<span>Bonus (+50)</span>
</div>
<div class="bubble-type">
<div class="bubble-demo" style="background: radial-gradient(circle at 30% 30%, #06d6a0, #04a777);"></div>
<span>Time (+5s)</span>
</div>
</div>
<div class="controls">
<button id="pause-btn" class="btn btn-pause">
<i class="fas fa-pause"></i> PAUSE
</button>
<button id="start-btn" class="btn">
<i class="fas fa-play"></i> START
</button>
</div>
<div class="instructions">
<strong>HOW TO PLAY:</strong> Tap bubbles to pop them. Each bubble type gives different points. Avoid letting bubbles escape off the screen!
</div>
</div>
<script>
// Game variables
let canvas, ctx;
let score = 0;
let timeLeft = 60;
let gameActive = false;
let gamePaused = false;
let animationId;
let bubbles = [];
let lastBubbleTime = 0;
let touchStartX = 0;
let touchStartY = 0;
// Bubble types with properties
const bubbleTypes = [
{ color: '#ff9a9e', points: 10, radius: 30, speed: 2, frequency: 0.5 }, // Normal
{ color: '#a18cd1', points: 20, radius: 25, speed: 4, frequency: 0.2 }, // Speed
{ color: '#ffd166', points: 50, radius: 40, speed: 1.5, frequency: 0.15 }, // Bonus
{ color: '#06d6a0', points: 0, radius: 35, speed: 2, frequency: 0.15 } // Time (special)
];
// Initialize game
function init() {
canvas = document.getElementById('game-canvas');
ctx = canvas.getContext('2d');
// Set canvas dimensions
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
// Event listeners for buttons
document.getElementById('start-btn').addEventListener('click', startGame);
document.getElementById('pause-btn').addEventListener('click', togglePause);
document.getElementById('restart-btn').addEventListener('click', restartGame);
// Touch events for the canvas
canvas.addEventListener('touchstart', handleTouchStart, { passive: false });
canvas.addEventListener('touchmove', handleTouchMove, { passive: false });
canvas.addEventListener('touchend', handleTouchEnd, { passive: false });
// Mouse events for desktop testing
canvas.addEventListener('mousedown', handleMouseDown);
// Draw initial screen
drawStartScreen();
}
// Resize canvas to fit container
function resizeCanvas() {
const container = canvas.parentElement;
canvas.width = container.clientWidth;
canvas.height = container.clientHeight;
if (!gameActive) {
drawStartScreen();
}
}
// Draw the start screen
function drawStartScreen() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw background gradient
const gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
gradient.addColorStop(0, 'rgba(26, 26, 46, 0.9)');
gradient.addColorStop(1, 'rgba(22, 33, 62, 0.9)');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw title
ctx.fillStyle = '#ffd166';
ctx.font = 'bold 40px Arial Rounded MT Bold, Arial, sans-serif';
ctx.textAlign = 'center';
ctx.fillText('BUBBLE POP FRENZY', canvas.width/2, canvas.height/2 - 40);
// Draw instructions
ctx.fillStyle = '#a1c4fd';
ctx.font = '20px Arial Rounded MT Bold, Arial, sans-serif';
ctx.fillText('Tap START to begin!', canvas.width/2, canvas.height/2 + 20);
// Draw sample bubbles
for (let i = 0; i < 5; i++) {
const x = canvas.width * 0.2 + i * canvas.width * 0.15;
const y = canvas.height * 0.7;
const radius = 25 + i * 5;
const color = bubbleTypes[i % bubbleTypes.length].color;
drawBubble(x, y, radius, color);
}
}
// Start the game
function startGame() {
if (gameActive && !gamePaused) return;
if (!gameActive) {
// Reset game state
score = 0;
timeLeft = 60;
bubbles = [];
lastBubbleTime = 0;
// Update UI
document.getElementById('score').textContent = score;
document.getElementById('timer').textContent = timeLeft;
document.getElementById('bubbles-count').textContent = bubbles.length;
document.getElementById('game-over').style.display = 'none';
gameActive = true;
gamePaused = false;
document.getElementById('pause-btn').innerHTML = '<i class="fas fa-pause"></i> PAUSE';
} else if (gamePaused) {
gamePaused = false;
document.getElementById('pause-btn').innerHTML = '<i class="fas fa-pause"></i> PAUSE';
}
// Start game loop
gameLoop();
}
// Toggle pause state
function togglePause() {
if (!gameActive) return;
gamePaused = !gamePaused;
if (gamePaused) {
document.getElementById('pause-btn').innerHTML = '<i class="fas fa-play"></i> RESUME';
cancelAnimationFrame(animationId);
} else {
document.getElementById('pause-btn').innerHTML = '<i class="fas fa-pause"></i> PAUSE';
gameLoop();
}
}
// Restart the game
function restartGame() {
document.getElementById('game-over').style.display = 'none';
startGame();
}
// Main game loop
function gameLoop(timestamp) {
if (!gameActive || gamePaused) return;
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw background
drawBackground();
// Spawn new bubbles
spawnBubbles(timestamp);
// Update and draw bubbles
updateBubbles();
// Update timer
updateTimer();
// Update UI
document.getElementById('score').textContent = score;
document.getElementById('timer').textContent = Math.ceil(timeLeft);
document.getElementById('bubbles-count').textContent = bubbles.length;
// Check if game is over
if (timeLeft <= 0) {
endGame();
return;
}
// Continue game loop
animationId = requestAnimationFrame(gameLoop);
}
// Draw the game background
function drawBackground() {
// Draw gradient background
const gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
gradient.addColorStop(0, 'rgba(10, 15, 35, 0.9)');
gradient.addColorStop(1, 'rgba(5, 10, 25, 0.9)');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw grid lines
ctx.strokeStyle = 'rgba(255, 255, 255, 0.05)';
ctx.lineWidth = 1;
// Vertical lines
for (let x = 0; x < canvas.width; x += 50) {
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x, canvas.height);
ctx.stroke();
}
// Horizontal lines
for (let y = 0; y < canvas.height; y += 50) {
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(canvas.width, y);
ctx.stroke();
}
}
// Spawn new bubbles
function spawnBubbles(timestamp) {
// Limit number of bubbles
if (bubbles.length > 30) return;
// Spawn new bubble occasionally
if (timestamp - lastBubbleTime > 500) { // Every 500ms
lastBubbleTime = timestamp;
// Determine which type of bubble to spawn based on frequency
const rand = Math.random();
let cumulativeFreq = 0;
let selectedType = 0;
for (let i = 0; i < bubbleTypes.length; i++) {
cumulativeFreq += bubbleTypes[i].frequency;
if (rand <= cumulativeFreq) {
selectedType = i;
break;
}
}
const type = bubbleTypes[selectedType];
// Create new bubble
const bubble = {
x: Math.random() * (canvas.width - type.radius * 2) + type.radius,
y: canvas.height + type.radius, // Start below the canvas
radius: type.radius,
color: type.color,
points: type.points,
speed: type.speed,
vx: (Math.random() - 0.5) * 2, // Random horizontal velocity
vy: -type.speed, // Move upward
type: selectedType
};
bubbles.push(bubble);
}
}
// Update all bubbles
function updateBubbles() {
for (let i = bubbles.length - 1; i >= 0; i--) {
const bubble = bubbles[i];
// Update position
bubble.x += bubble.vx;
bubble.y += bubble.vy;
// Bounce off walls
if (bubble.x - bubble.radius <= 0 || bubble.x + bubble.radius >= canvas.width) {
bubble.vx *= -0.9; // Reverse direction with some damping
bubble.x = Math.max(bubble.radius, Math.min(canvas.width - bubble.radius, bubble.x));
}
// Bounce off ceiling
if (bubble.y - bubble.radius <= 0) {
bubble.vy *= -0.9;
bubble.y = bubble.radius;
}
// Apply gravity
bubble.vy += 0.05;
// Remove bubbles that go below the screen
if (bubble.y - bubble.radius > canvas.height + 50) {
bubbles.splice(i, 1);
continue;
}
// Draw bubble
drawBubble(bubble.x, bubble.y, bubble.radius, bubble.color);
}
}
// Draw a bubble
function drawBubble(x, y, radius, color) {
// Bubble shine effect
const gradient = ctx.createRadialGradient(
x - radius/3, y - radius/3, 1,
x, y, radius
);
gradient.addColorStop(0, 'rgba(255, 255, 255, 0.8)');
gradient.addColorStop(0.3, color);
gradient.addColorStop(1, 'rgba(0, 0, 0, 0.3)');
// Draw bubble
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fillStyle = gradient;
ctx.fill();
// Bubble border
ctx.strokeStyle = 'rgba(255, 255, 255, 0.4)';
ctx.lineWidth = 2;
ctx.stroke();
// Bubble highlight
ctx.beginPath();
ctx.arc(x - radius/3, y - radius/3, radius/4, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(255, 255, 255, 0.6)';
ctx.fill();
}
// Update the game timer
function updateTimer() {
// Decrease time more slowly to make game last longer
timeLeft -= 0.016; // About 60 FPS
}
// Handle touch start
function handleTouchStart(e) {
e.preventDefault();
const touch = e.touches[0];
touchStartX = touch.clientX;
touchStartY = touch.clientY;
// Get canvas position
const rect = canvas.getBoundingClientRect();
const x = touch.clientX - rect.left;
const y = touch.clientY - rect.top;
checkBubblePop(x, y);
}
// Handle touch move
function handleTouchMove(e) {
e.preventDefault();
// For continuous popping while dragging finger
const touch = e.touches[0];
const rect = canvas.getBoundingClientRect();
const x = touch.clientX - rect.left;
const y = touch.clientY - rect.top;
checkBubblePop(x, y);
}
// Handle touch end
function handleTouchEnd(e) {
e.preventDefault();
}
// Handle mouse down (for desktop testing)
function handleMouseDown(e) {
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
checkBubblePop(x, y);
}
// Check if a bubble was popped
function checkBubblePop(x, y) {
if (!gameActive || gamePaused) return;
for (let i = bubbles.length - 1; i >= 0; i--) {
const bubble = bubbles[i];
const distance = Math.sqrt((x - bubble.x) ** 2 + (y - bubble.y) ** 2);
if (distance <= bubble.radius) {
// Pop the bubble
bubbles.splice(i, 1);
// Add score
score += bubble.points;
// Special effect for time bubble
if (bubble.type === 3) { // Time bubble
timeLeft += 5; // Add 5 seconds
// Visual feedback for time added
ctx.fillStyle = '#06d6a0';
ctx.font = 'bold 24px Arial';
ctx.textAlign = 'center';
ctx.fillText('+5s', bubble.x, bubble.y - bubble.radius - 10);
} else {
// Visual feedback for points
ctx.fillStyle = '#ffd166';
ctx.font = 'bold 24px Arial';
ctx.textAlign = 'center';
ctx.fillText(`+${bubble.points}`, bubble.x, bubble.y - bubble.radius - 10);
}
// Create pop effect
createPopEffect(bubble.x, bubble.y, bubble.color);
break; // Only pop one bubble per touch
}
}
}
// Create a pop effect when a bubble is popped
function createPopEffect(x, y, color) {
// Draw several small circles expanding outward
for (let i = 0; i < 8; i++) {
const angle = (i / 8) * Math.PI * 2;
const speed = 3;
const particle = {
x: x,
y: y,
radius: 5,
color: color,
vx: Math.cos(angle) * speed,
vy: Math.sin(angle) * speed,
life: 20 // frames
};
// Draw particles immediately
drawParticle(particle);
// Animate particles (simplified - would need particle system for full effect)
setTimeout(() => {
if (ctx) {
// Clear and redraw would be needed for proper animation
// This is a simplified version for demo
}
}, 50);
}
}
// Draw a particle
function drawParticle(particle) {
ctx.beginPath();
ctx.arc(particle.x, particle.y, particle.radius, 0, Math.PI * 2);
ctx.fillStyle = particle.color;
ctx.fill();
}
// End the game
function endGame() {
gameActive = false;
cancelAnimationFrame(animationId);
// Show game over screen
document.getElementById('final-score').textContent = score;
document.getElementById('game-over').style.display = 'flex';
// Add pulsing animation to restart button
document.getElementById('restart-btn').classList.add('pulse');
}
// Initialize the game when page loads
window.addEventListener('load', init);
</script>
</body>
</html>