<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>俄罗斯方块</title>
<style>
body {
background: #202028;
color: #fff;
font-family: sans-serif;
display: flex;
justify-content: center;
}
#game {
position: relative;
}
#board {
border: 2px solid #fff;
}
#score {
position: absolute;
top: -40px;
left: 0;
}
#next-block {
position: absolute;
top: 0;
right: -150px;
border: 2px solid #fff;
}
.control-panel {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
}
button {
padding: 10px 20px;
font-size: 18px;
background: #4CAF50;
border: none;
color: white;
cursor: pointer;
}
</style>
</head>
<body>
<div id="game">
<canvas id="board" width="300" height="600"></canvas>
<canvas id="next-block" width="120" height="120"></canvas>
<div id="score">得分: 0</div>
<div class="control-panel" id="controls">
<button onclick="startGame()">开始</button>
</div>
</div>
<script>
const BLOCKS = [
[[1,1,1,1]], // I
[[1,1,1],[0,1,0]], // T
[[1,1,1],[1,0,0]], // L
[[1,1,1],[0,0,1]], // J
[[1,1],[1,1]], // O
[[1,1,0],[0,1,1]], // S
[[0,1,1],[1,1,0]] // Z
];
const COLORS = ['#FF0D72', '#0DC2FF', '#0DFF72', '#F538FF', '#FF8E0D', '#FFE138', '#3877FF'];
const COLS = 10, ROWS = 20, BLOCK_SIZE = 30;
let ctx, nextCtx, board = [], score = 0;
let currentBlock, nextBlock, gameLoop, dropSpeed = 1000;
function initBoard() {
ctx = document.getElementById('board').getContext('2d');
nextCtx = document.getElementById('next-block').getContext('2d');
// 绘制网格
ctx.strokeStyle = '#303030';
for(let y = 0; y < ROWS; y++) {
for(let x = 0; x < COLS; x++) {
ctx.strokeRect(x*BLOCK_SIZE, y*BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);
}
}
}
function createBlock() {
const index = Math.floor(Math.random() * BLOCKS.length);
return {
shape: BLOCKS[index],
color: COLORS[index],
x: Math.floor(COLS/2) - 1,
y: 0
};
}
function drawBlock(block, context, offsetX=0, offsetY=0) {
context.fillStyle = block.color;
block.shape.forEach((row, y) => {
row.forEach((value, x) => {
if(value) {
context.fillRect(
(block.x + x + offsetX) * BLOCK_SIZE,
(block.y + y + offsetY) * BLOCK_SIZE,
BLOCK_SIZE-1, BLOCK_SIZE-1
);
}
});
});
}
function isValidMove(block, moveX=0, moveY=0, newShape) {
const shape = newShape || block.shape;
return shape.every((row, y) =>
row.every((value, x) => {
if(!value) return true;
const newX = block.x + x + moveX;
const newY = block.y + y + moveY;
return newX >= 0 && newX < COLS &&
newY < ROWS &&
!board[newY]?.[newX];
})
);
}
function rotateBlock() {
const newShape = currentBlock.shape[0].map((_, i) =>
currentBlock.shape.map(row => row[i]).reverse()
);
if(isValidMove(currentBlock, 0, 0, newShape)) {
currentBlock.shape = newShape;
}
}
function mergeBlock() {
currentBlock.shape.forEach((row, y) => {
row.forEach((value, x) => {
if(value) {
board[currentBlock.y + y][currentBlock.x + x] = currentBlock.color;
}
});
});
}
function clearLines() {
let linesCleared = 0;
for(let y = ROWS - 1; y >= 0; y--) {
if(board[y].every(cell => cell)) {
board.splice(y, 1);
board.unshift(Array(COLS).fill(null));
linesCleared++;
}
}
if(linesCleared) {
score += linesCleared * 100;
document.getElementById('score').textContent = `得分: ${score}`;
}
}
function drawBoard() {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
board.forEach((row, y) => {
row.forEach((color, x) => {
if(color) {
ctx.fillStyle = color;
ctx.fillRect(x*BLOCK_SIZE, y*BLOCK_SIZE, BLOCK_SIZE-1, BLOCK_SIZE-1);
}
ctx.strokeRect(x*BLOCK_SIZE, y*BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);
});
});
drawBlock(currentBlock, ctx);
}
function gameOver() {
clearInterval(gameLoop);
document.getElementById('controls').innerHTML = `
<div>游戏结束!得分: ${score}</div>
<button onclick="startGame()">重新开始</button>
`;
document.getElementById('controls').style.display = 'block';
}
function drop() {
currentBlock.y++;
if(!isValidMove(currentBlock)) {
currentBlock.y--;
mergeBlock();
clearLines();
currentBlock = nextBlock;
nextBlock = createBlock();
if(!isValidMove(currentBlock)) {
gameOver();
return;
}
drawNextBlock();
}
drawBoard();
}
function drawNextBlock() {
nextCtx.clearRect(0, 0, 120, 120);
nextCtx.strokeStyle = '#303030';
for(let y = 0; y < 4; y++) {
for(let x = 0; x < 4; x++) {
nextCtx.strokeRect(x*BLOCK_SIZE, y*BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);
}
}
drawBlock({...nextBlock, x:1, y:1}, nextCtx);
}
function startGame() {
document.getElementById('controls').style.display = 'none';
board = Array(ROWS).fill().map(() => Array(COLS).fill(null));
score = 0;
document.getElementById('score').textContent = `得分: 0`;
currentBlock = createBlock();
nextBlock = createBlock();
drawNextBlock();
drawBoard();
if(gameLoop) clearInterval(gameLoop);
gameLoop = setInterval(drop, dropSpeed);
}
document.addEventListener('keydown', e => {
e.preventDefault();
switch(e.key) {
case 'ArrowLeft':
if(isValidMove(currentBlock, -1)) currentBlock.x--; break;
case 'ArrowRight':
if(isValidMove(currentBlock, 1)) currentBlock.x++; break;
case 'ArrowUp':
rotateBlock(); break;
case 'ArrowDown':
drop(); break;
}
drawBoard();
});
initBoard();
</script>
</body>
</html>