<div id="tetris"></div>
HTML
格式化
支持Emmet,输入 p 后按 Tab键试试吧!
<head> ... </head>
<body>
</body>
body {
background-color: #cfd8dc;
}
#tetris {
display: inline-block;
}
#tetris div.ui {
display: inline-block;
margin: 5px;
margin-top: 10px;
vertical-align: top;
}
#tetris div.game {
display: inline-block;
}
#tetris table {
margin: 10px;
border-collapse: collapse;
-webkit-box-shadow: -5px 5px 15px #607d8b;
box-shadow: -5px 5px 15px #607d8b;
}
#tetris .glass tr:first-child {
display: none;
}
#tetris td {
width: 20px;
height: 20px;
background-color: #f5f5f5;
}
#tetris td.j {
background-color: #3f51b5;
}
#tetris td.l {
background-color: #4caf50;
}
#tetris td.o {
background-color: #e91e63;
}
#tetris td.i {
background-color: #009688;
}
#tetris td.s {
background-color: #9c27b0;
}
#tetris td.t {
background-color: #ffc107;
}
#tetris td.z {
background-color: #00bcd4;
}
#tetris .score-field {
font: 1.5em sans-serif;
display: inline-block;
margin: 15px;
padding: 5px;
border: 1px solid #607d8b;
background-color: #f5f5f5;
-webkit-box-shadow: -5px 5px 15px #607d8b;
box-shadow: -5px 5px 15px #607d8b;
}
/**
* @param options Parameters
* @param options.elem DOM element for component
* @param options.width Width of game field
* @param options.height Height of game field
*/
function Tetris(options) {
var elem = options.elem;
var width = (+options.width < 10) ? 10 : ~~(+options.width);
var height = (+options.height < 20) ? 21 : ~~(+options.height + 1);
var controlTimeoutHandle;
var fallingTimeoutHandle;
var predictionField;
var scoreField;
var gameField;
var tetraminoForms = [
[
['i', 'i', 'i', 'i']
],
[
['j', '.', '.'],
['j', 'j', 'j']
],
[
['.', '.', 'l'],
['l', 'l', 'l']
],
[
['o', 'o'],
['o', 'o']
],
[
['.', 's', 's'],
['s', 's', '.']
],
[
['.', 't', '.'],
['t', 't', 't']
],
[
['z', 'z', '.'],
['.', 'z', 'z']
]
];
var fieldArray = [];
var currentTetramino;
var nextTetramino;
var coordsToMove = [0, 0];
var fallSpeed = 1000;
function generateComponent() {
function generateUI() {
var divUi = document.createElement('div');
predictionField = document.createElement('table');
for (var i = 0; i < 4; i++) {
var tr = document.createElement('tr');
for (var j = 0; j < 4; j++) {
var td = document.createElement('td');
tr.appendChild(td);
}
predictionField.appendChild(tr);
}
scoreField = document.createElement('div');
scoreField.classList.add('score-field');
scoreField.innerHTML = 0;
divUi.appendChild(predictionField);
divUi.appendChild(scoreField);
divUi.classList.add('ui');
elem.appendChild(divUi);
}
function generateGlassUI() {
var divGameField = document.createElement('div');
gameField = document.createElement('table');
gameField.classList.add('glass')
for (var i = 0; i < height; i++) {
var tr = document.createElement('tr');
for (var j = 0; j < width; j++) {
var td = document.createElement('td');
tr.appendChild(td);
}
gameField.appendChild(tr);
}
divGameField.appendChild(gameField);
divGameField.classList.add('game');
elem.appendChild(divGameField);
}
function generateFieldArray() {
for (var i = 0; i < height; i++) {
fieldArray.push([]);
for (var j = 0; j < width; j++) {
fieldArray[i].push('.');
}
}
}
generateUI();
generateGlassUI();
generateFieldArray();
}
// ------------- Tetramino Class ------------------
function Tetramino(type) {
this.form = tetraminoForms[type].slice(0);
this.coords = [~~(width / 2) - ~~(tetraminoForms[type][0].length / 2), 0];
this.prevCoords;
}
Tetramino.prototype.rotate = function() {
var temp = new Array(this.form[0].length);
for (var i = 0; i < temp.length; i++) {
temp[i] = new Array(this.form.length);
for (var j = 0; j < temp[i].length; j++) {
temp[i][j] = this.form[temp[i].length - j - 1][i];
}
}
for (var i = 0; i < temp.length; i++) {
for (var j = 0; j < temp[i].length; j++) {
if (fieldArray[this.coords[1] + i] !== undefined &&
fieldArray[this.coords[1] + i][this.coords[0] + j] !== undefined &&
fieldArray[this.coords[1] + i][this.coords[0] + j] === '.') {
continue;
} else {
return;
}
}
}
this.form = temp;
};
Tetramino.prototype.control = function(coords) {
if (coords[0] === 0 && coords[1] === -1) {
this.rotate();
return;
}
if (this.coords[0] + coords[0] >= 0 &&
this.coords[0] + coords[0] + this.form[0].length <= fieldArray[0].length) {
if (checkSideCoords(this.coords[0] + coords[0], coords[0])) {
this.coords[0] += coords[0];
}
}
if (this.coords[1] + coords[1] >= 0 &&
this.coords[1] + coords[1] + this.form.length <= fieldArray.length) {
this.coords[1] += coords[1];
}
};
// ----------- Tetramino Class End ---------------
function drawField() {
for (var i = 0; i < fieldArray.length; i++) {
for (var j = 0; j < fieldArray[i].length; j++) {
gameField.rows[i].cells[j].className = '';
gameField.rows[i].cells[j].classList.add(fieldArray[i][j]);
}
}
}
function drawNextTetramino() {
for (var i = 0; i < 4; i++) {
for (var j = 0; j < 4; j++) {
predictionField.rows[i].cells[j].className = '';
}
}
for (var i = 0; i < nextTetramino.form.length; i++) {
for (var j = 0; j < nextTetramino.form[i].length; j++) {
predictionField.rows[i].cells[j].classList.add(nextTetramino.form[i][j]);
}
}
}
function controlTetramino(event) {
var code = event.keyCode;
if (code === 37) coordsToMove = [-1, 0];
if (code === 38) coordsToMove = [0, -1];
if (code === 39) coordsToMove = [1, 0];
if (code === 40) coordsToMove = [0, 1];
}
function runTetris() {
currentTetramino = new Tetramino(Math.floor(Math.random() * (6 + 1)));
nextTetramino = new Tetramino(Math.floor(Math.random() * (6 + 1)));
drawNextTetramino();
moveTetramino();
fallTetramino();
}
function moveTetramino() {
controlTimeoutHandle = setTimeout(function() {
if (currentTetramino.prevCoords) {
for (var i = 0; i < currentTetramino.form.length; i++) {
for (var j = 0; j < currentTetramino.form[i].length; j++) {
if (currentTetramino.form[i][j] == '.') continue;
fieldArray[i + currentTetramino.prevCoords[1]][j + currentTetramino.prevCoords[0]] = '.';
}
}
}
currentTetramino.control(coordsToMove);
for (var i = 0; i < currentTetramino.form.length; i++) {
for (var j = 0; j < currentTetramino.form[i].length; j++) {
if (currentTetramino.form[i][j] == '.') continue;
fieldArray[i + currentTetramino.coords[1]][j + currentTetramino.coords[0]] = currentTetramino.form[i][j];
}
}
currentTetramino.prevCoords = currentTetramino.coords.slice(0);
coordsToMove = [0, 0];
drawField();
checkBlocksUnderTetramino();
moveTetramino();
}, 0);
}
function fallTetramino() {
fallingTimeoutHandle = setTimeout(function() {
coordsToMove = [0, 1];
fallTetramino();
}, fallSpeed);
}
function checkBlocksUnderTetramino() {
if (currentTetramino.coords[1] + currentTetramino.form.length === fieldArray.length) {
changeTetramino();
return;
}
for (var i = currentTetramino.form.length - 1; i >= 0; i--) {
for (var j = currentTetramino.form[i].length - 1; j >= 0; j--) {
if (currentTetramino.form[i][j] === '.') continue;
if (fieldArray[currentTetramino.coords[1] + i + 1][currentTetramino.coords[0] + j] !== '.' &&
fieldArray[currentTetramino.coords[1] + i][currentTetramino.coords[0] + j] !== '.' &&
(currentTetramino.form[i + 1] === undefined || currentTetramino.form[i + 1][j] === '.')) {
changeTetramino();
return;
}
}
}
}
function checkSideCoords(coords, side) {
if (coords === 0 && coords === fieldArray[0].length) {
return false;
}
var side = (side === -1) ? -1 : currentTetramino.form[0].length;
for (var i = 0; i < currentTetramino.form.length; i++) {
if (fieldArray[currentTetramino.coords[1] + i][currentTetramino.coords[0] + side] !== '.' &&
currentTetramino.form[i][(side === -1) ? 0 : currentTetramino.form[0].length - 1] !== '.') {
return false;
}
}
return true;
}
function changeTetramino() {
checkAndRemoveFilledRow();
isGameOver();
currentTetramino = nextTetramino;
nextTetramino = new Tetramino(Math.floor(Math.random() * (6 + 1)));
drawNextTetramino();
scoreField.innerHTML++;
}
function checkAndRemoveFilledRow() {
for (var i = 0; i < fieldArray.length; i++) {
var toClean = true;
for (var j = 0; j < fieldArray[i].length; j++) {
if (fieldArray[i][j] === '.') {
toClean = false;
break;
}
}
if (toClean) {
fieldArray.splice(i, 1);
fieldArray.unshift([]);
for (var j = 0; j < width; j++) {
fieldArray[0].push('.');
}
scoreField.innerHTML = +scoreField.innerHTML + width;
fallSpeed = (fallSpeed > 300) ? fallSpeed - 50 : fallSpeed;
}
}
}
function isGameOver() {
if (currentTetramino.coords[1] <= 1) {
alert('game over');
resetGame();
}
}
function resetGame() {
for (var i = 0; i < fieldArray.length; i++) {
for (var j = 0; j < fieldArray[i].length; j++) {
fieldArray[i][j] = '.';
}
}
scoreField.innerHTML = 0;
fallSpeed = 1000;
}
generateComponent();
drawField();
document.body.addEventListener('keydown', controlTetramino, false);
runTetris();
}
var tetris = new Tetris({
elem: document.getElementById('tetris'),
width: 10,
height: 20
});
预览
控制台