<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>俄罗斯方块</title>
<style>
body {
background-color: #666;
}
#canvas {
display: block;
margin: 0 auto;
}
.tips {
text-align: center;
color: #fff;
margin-top: 10px;
font-size: 12px;
}
</style>
</head>
<body>
<canvas id="canvas" width="250" height="500"></canvas>
<div class="tips">↑旋转 ←向左移动 →向右移动 ↓向下移动</div>
<script>
window.addEventListener("load", () => {
const I = [
[
[1],
[1],
[1],
[1]
],
[
[1, 1, 1, 1]
]
]
const L = [
[
[2, 0],
[2, 0],
[2, 2]
],
[
[2, 2, 2],
[2, 0, 0]
],
[
[2, 2],
[0, 2],
[0, 2]
],
[
[0, 0, 2],
[2, 2, 2]
]
]
const J = [
[
[0, 3],
[0, 3],
[3, 3]
],
[
[3, 0, 0],
[3, 3, 3]
],
[
[3, 3],
[3, 0],
[3, 0]
],
[
[3, 3, 3],
[0, 0, 3]
]
]
const T = [
[
[4, 4, 4],
[0, 4, 0]
],
[
[0, 4],
[4, 4],
[0, 4]
],
[
[0, 4, 0],
[4, 4, 4]
],
[
[4, 0],
[4, 4],
[4, 0]
]
]
const S = [
[
[0, 5, 5],
[5, 5, 0]
],
[
[5, 0],
[5, 5],
[0, 5]
],
[
[0, 5, 5],
[5, 5, 0]
],
[
[5, 0],
[5, 5],
[0, 5]
]
]
const Z = [
[
[6, 6, 0],
[0, 6, 6]
],
[
[0, 6],
[6, 6],
[6, 0]
],
[
[6, 6, 0],
[0, 6, 6]
],
[
[0, 6],
[6, 6],
[6, 0]
]
]
const O = [
[
[7, 7],
[7, 7]
]
]
const ctx = canvas.getContext("2d");
const width = 10;
const height = 20;
let shapes = [];
let shapeIndex = 0;
let shape = [];
let grid = [];
let left = 0;
let top = 0;
ctx.scale(canvas.width / width, canvas.height / height);
function drawGrid() {
grid.forEach((row, y) => {
row.forEach((item, x) => {
if (item > 0) {
ctx.fillStyle = "#f00";
ctx.fillRect(x, y, 1, 1);
} else {
ctx.fillStyle = "#000";
ctx.fillRect(x, y, 1, 1);
}
})
})
}
function createGrid() {
grid = [];
for (let y = 0; y < height; y++) {
grid.push([]);
for (let x = 0; x < width; x++) {
grid[y].push(0);
}
}
}
function createRandomShape() {
let allShape = [I, L, J, T, S, Z, O];
shapes = allShape[Math.floor(Math.random() * allShape.length)];
shapeIndex = Math.floor(Math.random() * shapes.length);
shape = shapes[shapeIndex];
left = Math.floor((width - shape[0].length) / 2);
top = 0;
}
function allowTransformShape(nextShape) {
let result = true;
clearPreviousStep(left, top);
for (let y = 0; y < nextShape.length; y++) {
for (let x = 0; x < nextShape[0].length; x++) {
let item = nextShape[y][x];
if (item > 0) {
if (grid[y + top] === undefined || grid[y + top][x + left] !== 0) {
return false;
}
}
}
}
return result;
}
function transfromShape() {
let nextShapeIndex = -1;
if (shapeIndex + 1 === shapes.length) {
nextShapeIndex = 0
} else {
nextShapeIndex = shapeIndex + 1;
}
if (allowTransformShape(shapes[nextShapeIndex])) {
shapeIndex = nextShapeIndex;
shape = shapes[shapeIndex];
addCurrentStep(left, top);
drawGrid();
} else {
addCurrentStep(left, top);
}
}
function addCurrentStep(nextLeft, nextTop) {
for (let y = 0; y < shape.length; y++) {
for (let x = 0; x < shape[0].length; x++) {
let item = shape[y][x];
if (item > 0) {
grid[y + nextTop][x + nextLeft] = item;
} else {
continue;
}
}
}
left = nextLeft;
top = nextTop;
}
function clearPreviousStep(prevLeft, prevTop) {
for (let y = 0; y < shape.length; y++) {
for (let x = 0; x < shape[0].length; x++) {
let item = shape[y][x];
if (item > 0) {
grid[y + prevTop][x + prevLeft] = 0;
} else {
continue;
}
}
}
}
function allowNextStep(nextLeft, nextTop) {
let result = true;
for (let y = 0; y < shape.length; y++) {
for (let x = 0; x < shape[0].length; x++) {
let item = shape[y][x];
if (item > 0) {
// 左侧碰撞检测
if (nextLeft < left && shape[y][x - 1] !== item) {
if (grid[y + nextTop][x + nextLeft] !== 0) {
return false;
}
}
// 右侧碰撞检测
if (nextLeft > left && shape[y][x + 1] !== item) {
if (grid[y + nextTop][x + nextLeft] !== 0) {
return false;
}
}
// 底部碰撞检测
if (nextTop > top && (shape[y + 1] === undefined || shape[y + 1][x] === 0)) {
if (grid[y + nextTop] === undefined || grid[y + nextTop][x + nextLeft] !== 0) {
return false;
}
}
} else {
continue;
}
}
}
return result;
}
function move(x, y) {
if (allowNextStep(left + x, top + y)) {
clearPreviousStep(left, top);
addCurrentStep(left + x, top + y);
drawGrid();
} else {
if (y > 0) {
clearFullRow();
if (top === 0) {
restart();
return;
}
createRandomShape();
addCurrentStep(left, top);
drawGrid();
}
}
}
function restart() {
createGrid();
createRandomShape();
addCurrentStep(left, top);
drawGrid();
}
function clearFullRow() {
let arr = [];
grid.forEach((row, y) => {
if (row.every((item) => item > 0)) {
arr.push(y);
}
})
if (arr.length > 0) {
arr.forEach((y) => {
grid.splice(y, 1);
grid.unshift(new Array(width).fill(0));
})
}
}
createGrid();
createRandomShape();
addCurrentStep(left, top);
drawGrid();
window.addEventListener("keydown", function () {
switch (event.keyCode) {
case 38: /* 上 */
transfromShape();
break;
case 40: /* 下 */
move(0, 1);
break;
case 37: /* 左 */
move(-1, 0);
break;
case 39: /* 右 */
move(1, 0);
break;
}
})
let limit = 30;
let timestamp = 0;
function starAnimation() {
timestamp += 1;
if (timestamp > limit) {
timestamp = 0;
move(0, 1);
}
requestAnimationFrame(starAnimation);
}
starAnimation();
})
</script>
</body>
</html>