English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
The example is simple, the running address is: http://chendd.cn/demo/html/canvas/elsfk.html, it needs to support html5browser environment.
Functions implemented: block rotation (W key), automatic falling, movement (ASD), line clearing, quick falling (space bar), falling shadow, game over.
To achieve the following functions: scoring, levels, and different falling speeds for different levels when lines are cleared.
After learning from xiaoE's Java version of Tetris, I took the initiative to use html5implemented by canvas
The reference effect diagram is as follows:
The detailed code is as follows:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Tetris</title> <style type="text/css"> /*the entire canvas*/ #tetris { border: 6px solid grey; } /*game panel*/ </style> </head> <body> <canvas id="tetris" width="565" height="576></canvas> <script type="text/javascript"> var canvas = document.getElementById("tetris"); var context = canvas.getContext("2d"); var padding = 6, size = 32, minX = 0, maxX = 10, minY = 0, maxY = 18, score = 0, level = 1; var gameMap = new Array(); //Game map, two-dimensional array var gameTimer; initGameMap(); //Draw vertical lines drawGrid(); var arrays = basicBlockType(); var blockIndex = getRandomIndex(); //Randomly draw a block for the sake of it var block = getPointByCode(blockIndex); context.fillStyle = getBlockColorByIndex(blockIndex); drawBlock(block); /** * Initialize the game map */ function initGameMap() { for (var i = 0; i < maxY; i++) { var row = new Array(); for (var j = 0; j < maxX; j++) { row[j] = false; } gameMap[i] = row; } } /** * Block rotation * Clockwise: * A.x = O.y + O.x - B.y * A.y = O.y - O.x + B.x */ function round() { //The square block does not respond to rotation if (blockIndex == 4) { return; } //Loop to process the current block to find a new rotation point for (var i = 1; i < block.length; i++) { var o = block[0]; var point = block[i]; //The rotated position cannot conflict with the existing grid block var tempX = o.y + o.x - point.y; var tempY = o.y - o.x + point.x; if (isOverZone(tempX, tempY)) { return; //Cannot be rotated } } clearBlock(); //Can be rotated, set the new rotated coordinates for (var i = 1; i < block.length; i++) { var o = block[0]; var point = block[i]; //The rotated position cannot conflict with the existing grid block var tempX = o.y + o.x - point.y; var tempY = o.y - o.x + point.x; block[i] = { x: tempX, y: tempY }; } drawBlock(); } function moveDown() { var overFlag = canOver(); if(overFlag){ //If the block cannot move down any further, load the current block coordinates into the map window.clearInterval(gameTimer); add2GameMap(); //Clear different colored cells in the game area, redraw the map pile with a single color redrawGameMap(); return;//Game over } var flag = moveTo(0, 1); //If it can move, continue moving if (flag) { return; } //If the block cannot move down any further, load the current block coordinates into the map add2GameMap(); //Perform the line clearing action clearLines(); //Clear different colored cells in the game area, redraw the map pile with a single color redrawGameMap(); //If it cannot move down, continue to the next block nextBlock(); } /** * Line clearing action, returns the number of cleared lines */ function clearLines() { var clearRowList = new Array(); for (var i = 0; i < maxY; i++) { var flag = true; for (var j = 0; j < maxX; j++) { if (gameMap[i][j] == false) { flag = false; break; } } if (flag) { clearRowList.push(i); //Record the index of the cleared line numbers } } var clearRows = clearRowList.length; //The so-called line clearing is to move the indices of the rows to be cleared and all the cells below them up for (var x = 0; x < clearRows; x++) { var index = clearRowList[x]; for (var i = index; i > 0; i--) { for (var j = 0; j < maxX; j++) { gameMap[i][j] = gameMap[i - 1][j]; } } } if (clearRows > 0) { for (var i = 0; i < maxY; i++) { //Here you can limit the clearing operation of blocks that meet certain conditions && j < clearRowList[clearRows - 1] for (var j = 0; j < maxX; j++) { if (gameMap[i][j] == false) { clearBlockByPoint(i, j); } } } } } /** * Redraw the game map */ function redrawGameMap() { drawGrid(); for (var i = 0; i < maxY; i++) { for (var j = 0; j < maxX; j++) { if (gameMap[i][j]) { roadBlock(j, i); } } } } /** * Print the shadow map */ function drawShadowBlock() { var currentBlock = block; var shadowPoints = getCanMoveDown(); if (shadowPoints != null && shadowPoints.length > 0) { for (var i = 0; i < shadowPoints.length; i++) { var point = shadowPoints[i]; if (point == null) { continue; } var start = point.x * size; var end = point.y * size; context.fillStyle = "#abcdef"; context.fillRect(start, end, size, size); context.strokeStyle = "black"; context.strokeRect(start, end, size, size); } } } /** * Returns the maximum coordinate position that can be moved to (counts the total number of steps that can fall) * @return the maximum coordinate position that can be moved to */ function getCanMoveDown() { var nps = canMove(0, 1, block);}} var last = null; if (nps != null) { last = new Array(); while ((nps = canMove(0, 1, nps)) != null) { if (nps != null) { last = nps; } } } return last; } function canOver(){ var flag = false; for (var i = 0; i < block.length; i++) { var point = block[i]; var x = point.x; var y = point.y; if(isOverZone(x , y)){ flag = true; break; } } return flag; } function drawLevelScore() { } /** * Fill the map with immovable elements */ function add2GameMap() { for (var i = 0; i < block.length; i++) { var point = block[i]; var x = point.x; var y = point.y; var gameMapRow = gameMap[y]; //Get a row of the map gameMapRow[x] = true; //Mark a grid in this row as a pile gameMap[y] = gameMapRow; //Set the row back after adding } } function moveLeft() { moveTo(-1, 0); } function moveRight() { moveTo(1, 0); } function quickDown() { while (moveTo(0, 1)); } function moveTo(moveX, moveY) { var move = canMove(moveX, moveY, block); //Determine whether it is possible to move if (move == null) { return false; } clearBlock(); for (var i = 0; i < block.length; i++) { var point = block[i]; point.x = point.x + moveX; point.y = point.y + moveY; } drawBlock(); return true; } /** * Next block */ function nextBlock() { blockIndex = getRandomIndex(); block = getPointByCode(blockIndex); context.fillStyle = getBlockColorByIndex(blockIndex); drawBlock(); } document.onkeypress = function(evt) { var key = window.event ? evt.keyCode : evt.which; switch (key) { case 119: //Rotate upwards W round(); break; case 115: //Move down S moveDown(); break; case 97: //Move left A moveLeft(); break; case 100: //Move right D moveRight(); break; case 32: //Press the space bar to fall quickly to the bottom quickDown(); break; } } /** * Determine whether it is possible to move * @parammoveX The number of horizontal moves * @parammoveY The number of vertical moves */ function canMove(moveX, moveY, currentBlock) { var flag = true; var newPoints = new Array(); for (var i = 0; i < currentBlock.length; i++) { var point = currentBlock[i]; var tempX = point.x + moveX; var tempY = point.y + moveY; if (isOverZone(tempX, tempY)) { flag = false; break; } } if (flag) { for (var i = 0; i < currentBlock.length; i++) { var point = currentBlock[i]; var tempX = point.x + moveX; var tempY = point.y + moveY; newPoints[i] = { x: tempX, y: tempY }; } return newPoints; } return null; } /** * Determine whether it is possible to move * @paramx The horizontal coordinate after the preview move * @paramy The vertical coordinate after the preview move */ function isOverZone(x, y) { return x < minX || x >= maxX || y < minY || y >= maxY || gameMap[y][x]; } document.body.click(); gameTimer = window.setInterval(moveDown , 800); /** * Initialize the basic data of the block */ function basicBlockType() { var arrays = new Array(); arrays[0] = [{ x: 4, y: 0 }, { x: 3, y: 0 }, { x: 5, y: 0 }, { x: 6, y: 0 }]; arrays[1] = [{ x: 4, y: 0 }, { x: 3, y: 0 }, { x: 5, y: 0 }, { x: 4, y: 1 }]; arrays[2] = [{ x: 4, y: 0 }, { x: 3, y: 0 }, { x: 5, y: 0 }, { x: 3, y: 1 }]; arrays[3] = [{ x: 4, y: 0 }, { x: 5, y: 0 }, { x: 3, y: 1 }, { x: 4, y: 1 }]; arrays[4] = [{ x: 4, y: 0 }, { x: 5, y: 0 }, { x: 4, y: 1 }, { x: 5, y: 1 }]; arrays[5] = [{ x: 4, y: 0 }, { x: 3, y: 0 }, { x: 5, y: 0 }, { x: 5, y: 1 }]; arrays[6] = [{ x: 4, y: 0 }, { x: 3, y: 0 }, { x: 4, y: 1 }, { x: 5, y: 1 }]; return arrays; } function basicBlockColor() { return ["#A00000", "#A05000", "#A0A000", "#00A000", "#00A0A0", "#0000A0", "#A000A0"]; } function getBlockColorByIndex(typeCodeIndex) { var arrays = basicBlockColor(); return arrays[typeCodeIndex]; } /** * Return the specified block according to the number * @param typeCodeIndex Block number index */ function getPointByCode(typeCodeIndex) { var arrays = basicBlockType(); return arrays[typeCodeIndex]; } /** * Get the range value of the block that appears randomly * @param lens The range of the random number */ function getRandomIndex() { return parseInt(Math.random() * (arrays.length - 1), 10); } /** * Draw blocks, draw each grid individually */ function drawBlock() { drawGrid(); for (var i = 0; i < block.length; i++) { var point = block[i]; var start = point.x * size; var end = point.y * size; context.fillStyle = getBlockColorByIndex(blockIndex); context.fillRect(start, end, size, size); context.strokeStyle = "black"; context.strokeRect(start, end, size, size); } drawShadowBlock(); } /** * Draw an obstacle */ function roadBlock(x, y) { context.fillStyle = "darkgray"; var start = x * size; var end = y * size; context.fillRect(start, end, size, size); } /** * Draw a new block first clear the previous block */ function clearBlock() { for (var i = 0; i < block.length; i++) { var point = block[i]; var start = point.x * size; var end = point.y * size; context.clearRect(start, end, size, size); } } /** * Initialize a new row */ function initGameMapRow() { var array = new Array(); for (var i = 0; i < maxX; i++) { array[i] = false; } return array; } /** * Clear the content of the specified cell according to the coordinates * @paramx Horizontal coordinate * @paramy Vertical coordinate */ function clearBlockByPoint(x, y) { var start = y * size; var end = x * size; context.clearRect(start, end, size, size); } /** * Clear the drawing of all blank spaces at the location */ function clearAllNullPoint() { for (var i = 0; i < maxY; i++) { for (var j = 0; j < maxX; j++) { if (gameMap[i][j] == false) { clearBlockByPoint(i, j); } } } } /** * Draw grid lines * @paramcontext Drawing object */ function drawGrid() { clearAllNullPoint(); //Clear the shadow caused by the current block's falling position context.strokeStyle = "grey"; //Pen color for (var i = 0; i <= maxX; i++) { var start = i * size; var end = start + size; context.beginPath(); context.moveTo(start, 0); context.lineTo(size * i, size * maxY); context.stroke(); context.closePath(); } //Draw horizontal lines for (var i = 0; i <= maxY; i++) { var start = i * size; var end = start + size; context.beginPath(); context.moveTo(0, size * i); context.lineTo(size * maxX, size * i); context.stroke(); context.closePath(); } } </script> </body> </html>
That's all for HTML5 This is an instance of a Russian cube implemented, interested friends can refer to it, thank you all for your support to this site!