Загрузка данных
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Жизнь после смерти — эскиз</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #e8e8e8;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
font-family: 'Courier New', Courier, monospace;
padding: 20px;
}
.game-container {
background: #fdfdfd;
border-radius: 4px;
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.06);
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
}
canvas {
display: block;
background: #ffffff;
border: 1px solid #ccc;
border-radius: 2px;
cursor: default;
box-shadow: inset 0 0 20px rgba(0,0,0,0.02);
}
.hint {
margin-top: 14px;
font-size: 15px;
color: #555;
letter-spacing: 0.5px;
text-align: center;
user-select: none;
}
.hint kbd {
background: #f0f0f0;
border: 1px solid #bbb;
border-radius: 4px;
padding: 2px 8px;
font-family: inherit;
font-size: 13px;
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
}
.philosophy-note {
margin-top: 10px;
font-style: italic;
color: #777;
font-size: 13px;
text-align: center;
max-width: 500px;
line-height: 1.5;
}
</style>
</head>
<body>
<div class="game-container">
<canvas id="gameCanvas" width="900" height="500"></canvas>
<div class="hint">
<kbd>←</kbd> или <kbd>A</kbd> — идти влево | <kbd>→</kbd> или <kbd>D</kbd> — идти вправо
</div>
<div class="philosophy-note">
«Шаг за шагом по белому листу, где пол — лишь линия, а ты — лишь контур. Может быть, это и есть начало пути?»
</div>
</div>
<script>
(function() {
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// Параметры мира
const GROUND_Y = 390; // Y-координата поверхности пола
const GROUND_LEFT = 100; // Левая граница пола
const GROUND_RIGHT = 800; // Правая граница пола
const PLAYER_START_X = 450; // Начальная позиция человека
const PLAYER_HEIGHT = 180; // Высота человечка (от пола до макушки)
const HEAD_RADIUS = 22; // Радиус головы
const BODY_LENGTH = 80; // Длина туловища
const LEG_LENGTH = 50; // Длина ноги
const ARM_LENGTH = 55; // Длина руки
const MOVE_SPEED = 4.5; // Скорость движения
// Состояние игрока
let playerX = PLAYER_START_X;
let isMovingLeft = false;
let isMovingRight = false;
let walkCycle = 0; // Таймер для анимации ходьбы (в радианах)
// Ограничения движения
const MIN_X = GROUND_LEFT + 10;
const MAX_X = GROUND_RIGHT - 10;
// Обработчики клавиш
window.addEventListener('keydown', (e) => {
const key = e.key.toLowerCase();
if (key === 'arrowleft' || key === 'a') {
isMovingLeft = true;
e.preventDefault();
} else if (key === 'arrowright' || key === 'd') {
isMovingRight = true;
e.preventDefault();
}
});
window.addEventListener('keyup', (e) => {
const key = e.key.toLowerCase();
if (key === 'arrowleft' || key === 'a') {
isMovingLeft = false;
e.preventDefault();
} else if (key === 'arrowright' || key === 'd') {
isMovingRight = false;
e.preventDefault();
}
});
// Чтобы клавиши не прокручивали страницу
window.addEventListener('keydown', (e) => {
if (['arrowleft', 'arrowright', 'a', 'd'].includes(e.key.toLowerCase())) {
e.preventDefault();
}
}, { passive: false });
// Рисование пола — как карандашный штрих
function drawGround() {
ctx.save();
// Тонкая линия пола
ctx.beginPath();
ctx.moveTo(GROUND_LEFT, GROUND_Y);
ctx.lineTo(GROUND_RIGHT, GROUND_Y);
ctx.strokeStyle = '#2c2c2c';
ctx.lineWidth = 2.5;
ctx.setLineDash([]);
ctx.stroke();
// Лёгкая текстура «листа» — едва заметные штрихи
ctx.strokeStyle = '#f0f0f0';
ctx.lineWidth = 1;
for (let i = GROUND_LEFT; i < GROUND_RIGHT; i += 12) {
ctx.beginPath();
ctx.moveTo(i, GROUND_Y - 8);
ctx.lineTo(i + 6, GROUND_Y + 4);
ctx.stroke();
}
// Надпись-эскиз возле пола
ctx.font = 'italic 12px "Courier New", Courier, monospace';
ctx.fillStyle = '#aaa';
ctx.fillText('— пол (возможно, метафора)', GROUND_RIGHT - 190, GROUND_Y + 25);
ctx.restore();
}
// Рисование человечка — как набросок карандашом
function drawPlayer() {
ctx.save();
// Немного смещаем человечка при ходьбе для эффекта «оживления»
let bounce = 0;
if (isMovingLeft || isMovingRight) {
bounce = Math.sin(walkCycle * 2) * 1.5;
}
const headY = GROUND_Y - PLAYER_HEIGHT + HEAD_RADIUS + bounce;
const neckY = headY + HEAD_RADIUS + 2;
const hipY = neckY + BODY_LENGTH;
const footY = GROUND_Y;
// --- Ноги (с анимацией шага) ---
const legSwing = (isMovingLeft || isMovingRight) ? Math.sin(walkCycle) * 12 : 0;
// Левая нога
const leftLegAngle = legSwing;
const leftKneeX = playerX - 5;
const leftKneeY = hipY + LEG_LENGTH * 0.55;
const leftFootX = playerX - 8 + leftLegAngle * 0.8;
const leftFootY = footY;
ctx.beginPath();
ctx.moveTo(playerX - 4, hipY);
ctx.lineTo(leftKneeX, leftKneeY);
ctx.lineTo(leftFootX, leftFootY);
ctx.strokeStyle = '#1a1a1a';
ctx.lineWidth = 2.8;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.stroke();
// Правая нога
const rightLegAngle = -legSwing;
const rightKneeX = playerX + 5;
const rightKneeY = hipY + LEG_LENGTH * 0.55;
const rightFootX = playerX + 8 + rightLegAngle * 0.8;
const rightFootY = footY;
ctx.beginPath();
ctx.moveTo(playerX + 4, hipY);
ctx.lineTo(rightKneeX, rightKneeY);
ctx.lineTo(rightFootX, rightFootY);
ctx.stroke();
// Линия ступней (небольшие чёрточки)
ctx.beginPath();
ctx.moveTo(leftFootX - 6, leftFootY);
ctx.lineTo(leftFootX + 6, leftFootY);
ctx.moveTo(rightFootX - 6, rightFootY);
ctx.lineTo(rightFootX + 6, rightFootY);
ctx.lineWidth = 1.8;
ctx.stroke();
// --- Туловище ---
ctx.beginPath();
ctx.moveTo(playerX, neckY);
ctx.lineTo(playerX, hipY);
ctx.strokeStyle = '#1a1a1a';
ctx.lineWidth = 3.5;
ctx.stroke();
// --- Руки (слегка покачиваются при ходьбе) ---
const armSwing = (isMovingLeft || isMovingRight) ? Math.sin(walkCycle + Math.PI) * 8 : 0;
// Левая рука
ctx.beginPath();
ctx.moveTo(playerX - 1, neckY + 12);
ctx.lineTo(playerX - 14 + armSwing * 0.6, neckY + 38);
ctx.lineTo(playerX - 18 + armSwing * 0.9, neckY + 56);
ctx.lineWidth = 2.2;
ctx.stroke();
// Правая рука
ctx.beginPath();
ctx.moveTo(playerX + 1, neckY + 12);
ctx.lineTo(playerX + 14 - armSwing * 0.6, neckY + 38);
ctx.lineTo(playerX + 18 - armSwing * 0.9, neckY + 56);
ctx.stroke();
// --- Голова ---
ctx.beginPath();
ctx.arc(playerX, headY, HEAD_RADIUS, 0, Math.PI * 2);
ctx.strokeStyle = '#1a1a1a';
ctx.lineWidth = 2.5;
ctx.stroke();
// Лёгкая тень под подбородком (для эскизности)
ctx.beginPath();
ctx.arc(playerX, headY + HEAD_RADIUS * 0.3, HEAD_RADIUS * 0.7, 0.1, Math.PI - 0.1);
ctx.strokeStyle = '#999';
ctx.lineWidth = 0.8;
ctx.stroke();
// Глаза — две точки, смотрящие по направлению движения
const lookDirection = (isMovingLeft && !isMovingRight) ? -1 : (isMovingRight && !isMovingLeft) ? 1 : 0;
const eyeOffsetX = lookDirection * 3;
ctx.fillStyle = '#1a1a1a';
ctx.beginPath();
ctx.arc(playerX - 6 + eyeOffsetX, headY - 2, 2.2, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(playerX + 6 + eyeOffsetX, headY - 2, 2.2, 0, Math.PI * 2);
ctx.fill();
// Рот — маленькая чёрточка
ctx.beginPath();
ctx.moveTo(playerX - 4, headY + 8);
ctx.lineTo(playerX + 4, headY + 8);
ctx.strokeStyle = '#1a1a1a';
ctx.lineWidth = 1.2;
ctx.stroke();
ctx.restore();
}
// Обновление состояния
function update() {
// Определяем направление движения
const movingLeft = isMovingLeft && !isMovingRight;
const movingRight = isMovingRight && !isMovingLeft;
if (movingLeft) {
playerX -= MOVE_SPEED;
}
if (movingRight) {
playerX += MOVE_SPEED;
}
// Ограничение по краям пола
if (playerX < MIN_X) playerX = MIN_X;
if (playerX > MAX_X) playerX = MAX_X;
// Анимация ходьбы — увеличиваем фазу только при движении
if (movingLeft || movingRight) {
walkCycle += 0.18;
} else {
// Плавно возвращаем ноги в нейтральное положение
walkCycle *= 0.85;
}
}
// Отрисовка всего
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Белый лист — уже фон canvas, но добавим лёгкую текстуру
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Эффект «бумаги» — едва заметные разводы
ctx.fillStyle = 'rgba(0,0,0,0.008)';
for (let i = 0; i < 30; i++) {
ctx.beginPath();
ctx.arc(
100 + Math.random() * 700,
80 + Math.random() * 350,
Math.random() * 40 + 10,
0, Math.PI * 2
);
ctx.fill();
}
// Рисуем пол
drawGround();
// Рисуем человечка
drawPlayer();
// Подпись-философская заметка (лёгкий эскиз)
ctx.font = 'italic 13px "Courier New", Courier, monospace';
ctx.fillStyle = '#bbb';
ctx.fillText('...и кто идёт по этой линии?', 280, 130);
}
// Игровой цикл
function gameLoop() {
update();
draw();
requestAnimationFrame(gameLoop);
}
// Запуск
gameLoop();
// Также поддержка касаний для мобильных (просто заглушка)
// Можно добавить кнопки, но по условию — клавиатура.
})();
</script>
</body>
</html>