🌸 Гран-При 18 лет 🌸
by SolarBear25641 lines25.8 KB
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>🌸 Гран-При 18 лет 🌸</title>
<style>
/* ГЛОБАЛЬНЫЙ РОЗОВЫЙ СТИЛЬ */
* {
box-sizing: border-box;
user-select: none;
}
body {
margin: 0;
min-height: 100vh;
background: linear-gradient(145deg, #1a0b1a 0%, #2d142c 40%, #4a1e3f 100%);
display: flex;
justify-content: center;
align-items: center;
font-family: 'Segoe UI', 'Arial', sans-serif;
touch-action: manipulation;
}
.game-wrapper {
background: rgba(255, 220, 240, 0.1);
backdrop-filter: blur(4px);
padding: 20px;
border-radius: 60px 60px 30px 30px;
box-shadow: 0 0 60px rgba(255, 20, 147, 0.5), inset 0 0 30px rgba(255, 105, 180, 0.3);
border: 2px solid #ff69b4;
}
.container {
background: #fce4ec;
background-image: radial-gradient(circle at 10% 20%, rgba(255, 182, 193, 0.4) 0%, transparent 50%);
border-radius: 40px;
padding: 20px;
max-width: 420px;
margin: 0 auto;
box-shadow: inset 0 0 30px #ffb6c1;
border: 1px solid #ff85a2;
}
/* ШАПКА ИГРЫ */
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 5px 15px 5px;
color: #8b1a4a;
font-weight: 900;
text-shadow: 0 2px 0 #ffb6c1;
}
.score-box {
background: #4a1a2e;
padding: 8px 20px;
border-radius: 50px;
color: #ffb0d0;
font-size: 1.4rem;
letter-spacing: 1px;
box-shadow: 0 0 15px #ff1493;
border: 1px solid #ff69b4;
}
.lives {
font-size: 2rem;
line-height: 1;
}
/* Холст (Трасса) */
canvas {
display: block;
width: 100%;
height: auto;
aspect-ratio: 360 / 600;
background: #2b1a22;
border-radius: 30px;
box-shadow: 0 0 0 3px #ff69b4, 0 0 0 6px #9b4d6b;
cursor: pointer;
touch-action: none;
margin-bottom: 10px;
}
/* Кнопки управления (для мобильных) */
.controls {
display: flex;
justify-content: space-between;
gap: 10px;
padding-top: 10px;
}
.ctrl-btn {
background: #7a2b4f;
border: 2px solid #ff85a2;
color: #ffe0ec;
font-size: 2.2rem;
padding: 12px 0;
border-radius: 60px;
flex: 1;
text-align: center;
font-weight: bold;
box-shadow: 0 5px 0 #3f172b;
transition: all 0.08s ease;
touch-action: manipulation;
cursor: pointer;
letter-spacing: 2px;
}
.ctrl-btn:active {
transform: translateY(5px);
box-shadow: 0 1px 0 #3f172b;
}
/* ЭКРАН ФИНАЛА (ПИСЬМО + ХЛОПУШКИ) */
.final-screen {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(20, 5, 18, 0.9);
backdrop-filter: blur(12px);
z-index: 999;
justify-content: center;
align-items: center;
flex-direction: column;
padding: 20px;
animation: fadeInPop 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.final-screen.show {
display: flex;
}
.letter {
max-width: 500px;
width: 100%;
background: linear-gradient(135deg, #fff5f9, #ffe4ec);
padding: 40px 30px;
border-radius: 80px 20px 80px 20px;
box-shadow: 0 0 80px #ff1493, 0 20px 40px rgba(0,0,0,0.8);
border: 3px solid #ff69b4;
text-align: center;
color: #3b1c2b;
position: relative;
transform: scale(1);
transition: 0.3s;
font-size: 1rem;
}
.letter h1 {
font-size: 2.8rem;
margin: 0 0 5px 0;
background: linear-gradient(45deg, #d61a6b, #ff1493, #ff69b4);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
text-shadow: none;
font-weight: 900;
}
.letter .sub {
font-size: 1.2rem;
background: #8b1a4a;
color: #ffc0d0;
padding: 5px 20px;
border-radius: 50px;
display: inline-block;
margin-bottom: 15px;
-webkit-text-fill-color: #ffd1dc;
}
.letter p {
font-size: 1.2rem;
line-height: 1.6;
font-weight: 500;
}
.letter .emoji-big {
font-size: 4rem;
display: block;
margin: 10px 0;
}
.btn-restart {
background: #d61a6b;
border: none;
padding: 15px 40px;
border-radius: 50px;
font-size: 1.4rem;
font-weight: bold;
color: white;
margin-top: 20px;
box-shadow: 0 0 30px #ff1493;
cursor: pointer;
transition: 0.2s;
border: 2px solid #ff85a2;
}
.btn-restart:active {
transform: scale(0.9);
}
/* АНИМАЦИЯ КОНФЕТТИ И ХЛОПУШЕК */
.confetti-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 1000;
overflow: hidden;
}
.confetti {
position: absolute;
width: 14px;
height: 14px;
opacity: 0.9;
animation: confettiFall linear forwards;
}
@keyframes confettiFall {
0% { transform: translateY(-10vh) rotate(0deg) scale(1); opacity: 1; }
100% { transform: translateY(110vh) rotate(720deg) scale(0.3); opacity: 0; }
}
@keyframes fadeInPop {
0% { opacity: 0; transform: scale(0.5); }
100% { opacity: 1; transform: scale(1); }
}
/* Адаптив */
@media (max-width: 480px) {
.letter h1 { font-size: 2.2rem; }
.letter p { font-size: 1rem; }
.game-wrapper { padding: 10px; }
.container { padding: 12px; }
}
</style>
</head>
<body>
<!-- ИГРОВОЙ БЛОК -->
<div class="game-wrapper">
<div class="container">
<div class="header">
<span class="score-box">💖 <span id="scoreDisplay">0</span>/18</span>
<span class="lives" id="livesDisplay">❤️❤️❤️</span>
</div>
<canvas id="gameCanvas" width="360" height="600"></canvas>
<div class="controls">
<div class="ctrl-btn" id="btnLeft">◀ ЛЕВО</div>
<div class="ctrl-btn" id="btnRight">ПРАВО ▶</div>
</div>
<p style="text-align:center; color:#8b1a4a; font-weight:bold; margin:8px 0 0 0; font-size:0.9rem;">
🏁 Собери 18 💖, объезжай 🚗! 🏁
</p>
</div>
</div>
<!-- ЭКРАН С ПИСЬМОМ (ФИНАЛ) -->
<div class="final-screen" id="finalScreen">
<div class="letter">
<div class="emoji-big">🏎️💨</div>
<h1>С 18-ЛЕТИЕМ!</h1>
<span class="sub">🌸 Гран-При Твоей Жизни 🌸</span>
<p>
<strong>Наша Розовая Гонщица!</strong><br><br>
Сегодня ты пересекаешь финишную черту детства<br>
и входишь на трассу взрослой жизни на полном газу! 🏁<br><br>
Пусть твой путь будет быстрым, как формула-1,<br>
но таким же красивым и стильным, как этот розовый болид.<br>
<span style="font-size:1.8rem;">💖🔥</span><br><br>
Желаю тебе миллион побед, верных механиков (друзей),<br>
бесконечного топлива счастья и только ярких эмоций!<br>
Ты — наша чемпионка! 🥂✨
</p>
<p style="font-style:italic; color:#a1416b;">С любовью, твоя команда поддержки ❤️</p>
<button class="btn-restart" id="restartBtn">🔁 ПРОКРУТИТЬ ЗАНОВО</button>
</div>
</div>
<!-- КОНТЕЙНЕР ДЛЯ КОНФЕТТИ -->
<div class="confetti-container" id="confettiContainer"></div>
<script>
(function() {
// === НАСТРОЙКИ ===
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreSpan = document.getElementById('scoreDisplay');
const livesSpan = document.getElementById('livesDisplay');
const finalScreen = document.getElementById('finalScreen');
const confettiContainer = document.getElementById('confettiContainer');
const W = 360, H = 600;
canvas.width = W; canvas.height = H;
// Игрок с параметрами плавности
const player = {
x: 180,
y: 480,
radius: 20,
vx: 0,
acceleration: 0.7,
friction: 0.92,
maxSpeed: 10
};
// Состояние игры
let score = 0;
let lives = 3;
let gameOver = false;
let winFlag = false;
// Массивы
let hearts = [];
let obstacles = [];
let frame = 0;
// Управление
let leftPressed = false;
let rightPressed = false;
// === ФУНКЦИЯ СОЗДАНИЯ ОБЪЕКТОВ ===
function spawnHeart() {
const x = 30 + Math.random() * (W - 60);
hearts.push({
x: x,
y: -20,
radius: 14,
speed: 1.5 + Math.random() * 1.5
});
}
function spawnObstacle() {
const x = 30 + Math.random() * (W - 60);
const carEmojis = ['🚗', '🚕', '🚙', '🚌', '🚓'];
const emoji = carEmojis[Math.floor(Math.random() * carEmojis.length)];
obstacles.push({
x: x,
y: -20,
radius: 20,
speed: 1.2 + Math.random() * 1.8,
emoji: emoji
});
}
// === СБРОС ИГРЫ ===
function resetGame() {
score = 0;
lives = 3;
gameOver = false;
winFlag = false;
hearts = [];
obstacles = [];
player.x = 180;
player.vx = 0;
leftPressed = false;
rightPressed = false;
finalScreen.classList.remove('show');
confettiContainer.innerHTML = '';
updateUI();
// Восстанавливаем текст письма на оригинальный
document.querySelector('.letter h1').textContent = 'С 18-ЛЕТИЕМ!';
document.querySelector('.letter .sub').textContent = '🌸 Гран-При Твоей Жизни 🌸';
document.querySelector('.letter p').innerHTML = `
<strong>Наша Розовая Гонщица!</strong><br><br>
Сегодня ты пересекаешь финишную черту детства<br>
и входишь на трассу взрослой жизни на полном газу! 🏁<br><br>
Пусть твой путь будет быстрым, как формула-1,<br>
но таким же красивым и стильным, как этот розовый болид.<br>
<span style="font-size:1.8rem;">💖🔥</span><br><br>
Желаю тебе миллион побед, верных механиков (друзей),<br>
бесконечного топлива счастья и только ярких эмоций!<br>
Ты — наша чемпионка! 🥂✨
`;
}
// === ОБНОВЛЕНИЕ UI ===
function updateUI() {
scoreSpan.textContent = Math.min(score, 18);
let heartsStr = '';
for (let i = 0; i < lives; i++) heartsStr += '❤️';
if (lives === 0) heartsStr = '💔💔💔';
livesSpan.textContent = heartsStr || '💀';
}
// === СТОЛКНОВЕНИЯ ===
function distance(x1, y1, x2, y2) {
return Math.hypot(x1 - x2, y1 - y2);
}
// === ЗАПУСК КОНФЕТТИ ===
function launchConfetti() {
confettiContainer.innerHTML = '';
const colors = ['#ff1493', '#ff69b4', '#ffb6c1', '#ff85a2', '#d61a6b', '#ffc0cb', '#ffffff', '#ffd700'];
const shapes = ['●', '■', '▲', '★', '♦', '♥'];
for (let i = 0; i < 150; i++) {
const el = document.createElement('div');
el.className = 'confetti';
el.textContent = shapes[Math.floor(Math.random() * shapes.length)];
el.style.left = Math.random() * 100 + '%';
el.style.color = colors[Math.floor(Math.random() * colors.length)];
el.style.fontSize = (10 + Math.random() * 24) + 'px';
el.style.animationDuration = (2 + Math.random() * 3) + 's';
el.style.animationDelay = (Math.random() * 2) + 's';
el.style.transform = `rotate(${Math.random()*360}deg)`;
confettiContainer.appendChild(el);
}
for (let i = 0; i < 30; i++) {
setTimeout(() => {
const pop = document.createElement('div');
pop.className = 'confetti';
pop.textContent = '✨';
pop.style.left = Math.random() * 100 + '%';
pop.style.color = '#fff9c4';
pop.style.fontSize = (30 + Math.random() * 40) + 'px';
pop.style.animationDuration = (1 + Math.random()) + 's';
pop.style.animationDelay = '0s';
pop.style.opacity = '1';
confettiContainer.appendChild(pop);
setTimeout(() => pop.remove(), 1200);
}, i * 80);
}
}
// === ПОБЕДА (18 сердец) ===
function winGame() {
if (winFlag) return;
winFlag = true;
gameOver = true;
launchConfetti();
setTimeout(() => {
finalScreen.classList.add('show');
}, 400);
}
// === ПОТЕРЯ ЖИЗНИ ===
function loseLife() {
if (gameOver) return;
lives--;
updateUI();
if (lives <= 0) {
gameOver = true;
setTimeout(() => {
launchConfetti();
finalScreen.classList.add('show');
document.querySelector('.letter h1').textContent = '💪 НЕ СДАВАЙСЯ!';
document.querySelector('.letter .sub').textContent = '🌸 Ты всё равно чемпионка! 🌸';
document.querySelector('.letter p').innerHTML = `
<strong>Даже если машинки мешали,</strong><br>
ты уже победила своим характером! 🏎️<br><br>
В 18 лет все дороги открыты,<br>
а мы всегда рядом, чтобы подтолкнуть вперёд!<br>
<span style="font-size:2rem;">💖🚀</span><br><br>
С днём рождения! Ты — лучшая!
`;
}, 300);
}
}
// === ОБНОВЛЕНИЕ ИГРЫ ===
function update() {
if (gameOver) return;
// Плавное движение
let accel = 0;
if (leftPressed) accel = -player.acceleration;
if (rightPressed) accel = player.acceleration;
player.vx += accel;
player.vx *= player.friction;
if (Math.abs(player.vx) < 0.05) player.vx = 0;
if (Math.abs(player.vx) > player.maxSpeed) {
player.vx = Math.sign(player.vx) * player.maxSpeed;
}
player.x += player.vx;
if (player.x - player.radius < 5) {
player.x = 5 + player.radius;
player.vx = 0;
}
if (player.x + player.radius > W - 5) {
player.x = W - 5 - player.radius;
player.vx = 0;
}
// Спавн
frame++;
if (frame % 35 === 0 && hearts.length < 12) spawnHeart();
if (frame % 45 === 0 && obstacles.length < 7) spawnObstacle();
// Сердечки
for (let i = hearts.length - 1; i >= 0; i--) {
const h = hearts[i];
h.y += h.speed;
if (distance(player.x, player.y, h.x, h.y) < player.radius + h.radius) {
hearts.splice(i, 1);
score++;
updateUI();
if (score >= 18) { // ← ИЗМЕНЕНО НА 18
winGame();
}
continue;
}
if (h.y > H + 30) {
hearts.splice(i, 1);
}
}
// Машинки-препятствия
for (let i = obstacles.length - 1; i >= 0; i--) {
const o = obstacles[i];
o.y += o.speed;
if (distance(player.x, player.y, o.x, o.y) < player.radius + o.radius) {
obstacles.splice(i, 1);
loseLife();
continue;
}
if (o.y > H + 30) {
obstacles.splice(i, 1);
}
}
}
// === РИСОВАНИЕ ===
function draw() {
ctx.clearRect(0, 0, W, H);
// Трасса
ctx.fillStyle = '#2b1a22';
ctx.fillRect(0, 0, W, H);
for (let y = 0; y < H; y += 40) {
for (let x = 0; x < W; x += 40) {
if ((Math.floor(x/40) + Math.floor(y/40)) % 2 === 0) {
ctx.fillStyle = 'rgba(255, 105, 180, 0.15)';
ctx.fillRect(x, y, 40, 40);
}
}
}
ctx.strokeStyle = '#ff69b4';
ctx.lineWidth = 6;
ctx.setLineDash([15, 20]);
ctx.beginPath();
ctx.moveTo(10, 0);
ctx.lineTo(10, H);
ctx.moveTo(W-10, 0);
ctx.lineTo(W-10, H);
ctx.stroke();
ctx.setLineDash([]);
// Сердечки
for (let h of hearts) {
ctx.shadowColor = '#ff1493';
ctx.shadowBlur = 25;
ctx.font = '28px "Segoe UI", Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = '#ff1493';
ctx.fillText('💖', h.x, h.y);
ctx.shadowBlur = 0;
}
// Машинки
for (let o of obstacles) {
ctx.shadowColor = '#ff4500';
ctx.shadowBlur = 20;
ctx.font = '32px "Segoe UI", Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = '#ff6347';
ctx.fillText(o.emoji, o.x, o.y);
ctx.shadowBlur = 0;
}
// Игрок
ctx.shadowColor = '#ff1493';
ctx.shadowBlur = 40;
ctx.font = '44px "Segoe UI", Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = '#ff1493';
ctx.fillText('🏎️', player.x, player.y);
ctx.shadowBlur = 0;
// Тень
ctx.beginPath();
ctx.arc(player.x, player.y+25, 15, 0, Math.PI*2);
ctx.fillStyle = 'rgba(255,20,147,0.2)';
ctx.filter = 'blur(8px)';
ctx.fill();
ctx.filter = 'none';
// Финишная черта с 18
ctx.fillStyle = '#ff69b4';
ctx.font = 'bold 16px Arial';
ctx.fillText('🏁 ФИНИШ (18💖) 🏁', W/2, 25);
}
// === ИГРОВОЙ ЦИКЛ ===
function gameLoop() {
update();
draw();
requestAnimationFrame(gameLoop);
}
// === УПРАВЛЕНИЕ ===
function handleKeyDown(e) {
const key = e.key;
if (key === 'ArrowLeft' || key === 'Left' || key === 'a' || key === 'A') {
leftPressed = true;
e.preventDefault();
} else if (key === 'ArrowRight' || key === 'Right' || key === 'd' || key === 'D') {
rightPressed = true;
e.preventDefault();
}
}
function handleKeyUp(e) {
const key = e.key;
if (key === 'ArrowLeft' || key === 'Left' || key === 'a' || key === 'A') {
leftPressed = false;
e.preventDefault();
} else if (key === 'ArrowRight' || key === 'Right' || key === 'd' || key === 'D') {
rightPressed = false;
e.preventDefault();
}
}
// Мобильные кнопки
document.getElementById('btnLeft').addEventListener('touchstart', (e) => { e.preventDefault(); leftPressed = true; });
document.getElementById('btnLeft').addEventListener('touchend', (e) => { e.preventDefault(); leftPressed = false; });
document.getElementById('btnLeft').addEventListener('mousedown', () => { leftPressed = true; });
document.getElementById('btnLeft').addEventListener('mouseup', () => { leftPressed = false; });
document.getElementById('btnLeft').addEventListener('mouseleave', () => { leftPressed = false; });
document.getElementById('btnRight').addEventListener('touchstart', (e) => { e.preventDefault(); rightPressed = true; });
document.getElementById('btnRight').addEventListener('touchend', (e) => { e.preventDefault(); rightPressed = false; });
document.getElementById('btnRight').addEventListener('mousedown', () => { rightPressed = true; });
document.getElementById('btnRight').addEventListener('mouseup', () => { rightPressed = false; });
document.getElementById('btnRight').addEventListener('mouseleave', () => { rightPressed = false; });
document.getElementById('restartBtn').addEventListener('click', resetGame);
window.addEventListener('keydown', handleKeyDown);
window.addEventListener('keyup', handleKeyUp);
resetGame();
gameLoop();
})();
</script>
</body>
</html>Game Source: 🌸 Гран-При 18 лет 🌸
Creator: SolarBear25
Libraries: none
Complexity: complex (641 lines, 25.8 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: 18-solarbear25" to link back to the original. Then publish at arcadelab.ai/publish.