首页 > 代码库 > 我也来写一个俄罗斯方块

我也来写一个俄罗斯方块

  上周末写好了大部分功能函数,今天趁工作做完,把剩下的功能写好。下面先给出效果图:

    技术分享

  代码如下:

<!doctype html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="format-detection" content="telephone=no">
<style>
	body {
		position: relative;
		margin: 0;
		width: 100vw;
		height: 100vh;
	}
	.wrapper {
		padding: 40px 0;
	}
	.tetris-box {
		margin: 0 auto;
		border-collapse: collapse; 
		border: solid #ddd; 
		border-width: 1px 0px 0px 1px;
	}
	.tetris-box td {
		padding: 10px 9px 9px 10px;
		border: solid #ddd; 
		border-width: 0px 1px 1px 0px; 
	}
	.cell {
	    box-shadow: inset 1px 1px 0px 4px rgba(0, 0, 0, 0.4);
	    /*border: solid #f10 !important;
    	border-width: 0px 1px 1px 0px !important;*/
	}
	.color1 {
		background-image: linear-gradient(45deg, #f10, #fff 50%, #f10 50%);
	}
	.color2 {
		background-image: linear-gradient(-45deg, #00b0ff 35%, #fff 50%, #00b0ff 65%);
	}
	.color3 {
		background-image: linear-gradient(45deg, #dcad6b 50%, #fff 50%, #dcad6b 100%);
	}
	.color4 {
		background-image: linear-gradient(135deg, #6bdc7d, #fff 55%, #6bdc7d 50%);
	}
	.color5 {
		background-image: linear-gradient(-135deg, #d46bdc, #fff 55%, #d46bdc 50%);
	}
	.color6 {
		background-image: radial-gradient(#00b0ff, #fff 60%, #00b0ff 50%);
	}
	.color7 {
	    background-image: radial-gradient(#e6d7d7, #fff 85%, #e6d7d7 100%);
	}
	.btn-box {
		text-align: center;
		margin-bottom: 30px;
	}
</style>
</head>
<body>
<div class="wrapper">
	<div class="btn-box">
		<p>可以用空格键控制开始、暂停、继续</p>
	</div>
	<table id="tetris_box" class="tetris-box">
	</table>
</div>
<script>
//Array扩展indexOf
Array.prototype.indexOf = function(val) {
    for (var i = 0; i < this.length; i++) {
        if (this[i] == val) return i;
    }
    return -1;
};

(function() {
	// 基本配置
	function settings() {
		var settings = {
			width: 10,
			height: 20,
			speed: 500
		}
		return settings;
	}

	var o = new settings();

	var doc = document;  //保存全局document引用

	// 存储需要重复使用的全局变量
	var g = { 
		tetrisArray: [],  //保存每个格子的颜色
		savedArray: [],  //保存已经固定的图形的信息
		graphArray: [],  //保存当前图形的数据
		color: 0,  //保存上次的颜色
		shape: 0, //保存上次的形状
		y: {‘isY‘: 0, ‘time‘: 0},  //保存需要第二次旋转y补1的图形信息 isY:是否需要补y,time:是否旋转 
		start: 0,
		timer: null  //计时器
	}
	
	// 构建UI
	var tetrisBox = doc.getElementById(‘tetris_box‘);
	tetrisBox.innerHTML = null;
	var fragment = doc.createDocumentFragment();
	for (var j = 0; j < o.height; j++) {
		var hTr = doc.createElement(‘tr‘);
		hTr.setAttribute(‘class‘, ‘row‘);
		var cellFragment = doc.createDocumentFragment();
		g.tetrisArray[j] = [];
		for (var i = 0; i < o.width; i++) {
			var wTd = doc.createElement(‘td‘);
			var wId = j + ‘_‘ + i;
			wTd.setAttribute(‘id‘, wId);
			// wTd.setAttribute(‘class‘, ‘cell‘);
			cellFragment.appendChild(wTd);
			g.tetrisArray[j][i] = -1;
		}
		fragment.appendChild(hTr).appendChild(cellFragment);
	}
	tetrisBox.appendChild(fragment);

	// 随机选择一个形状
	function createGraph() {
		var chance = Math.ceil(Math.random() * 7) - 1;
		// chance = 0;
		g.graphArray.length = 0;
		switch(chance) {
			/* 
				****
			*/
			case 0:
				g.graphArray.push({x: 3, y: 3}, {x: 3, y: 4}, {x: 3, y: 5}, {x: 3, y: 6});
				break;
			/* 
				**
				**
			*/
			case 1:
				g.graphArray.push({x: 2, y: 4}, {x: 2, y: 5}, {x: 3, y: 4}, {x: 3, y: 5});
				break;
			/* 
				 **
				**
			*/
			case 2:
				g.graphArray.push({x: 2, y: 4}, {x: 2, y: 5}, {x: 3, y: 3}, {x: 3, y: 4});
				break;
			/* 
				**
				 **
			*/
			case 3:
				g.graphArray.push({x: 2, y: 3}, {x: 2, y: 4}, {x: 3, y: 4}, {x: 3, y: 5});
				break;
			/* 
				***
				  *
			*/
			case 4:
				g.graphArray.push({x: 2, y: 3}, {x: 2, y: 4}, {x: 2, y: 5}, {x: 3, y: 5});
				break;
			/* 
				***
				*
			*/
			case 5:
				g.graphArray.push({x: 2, y: 3}, {x: 2, y: 4}, {x: 2, y: 5}, {x: 3, y: 3});
				break;
			/* 
				 *
				***
			*/
			case 6:
				g.graphArray.push({x: 2, y: 3}, {x: 2, y: 4}, {x: 2, y: 5}, {x: 1, y: 4});
				break;
		}
		g.shape = chance;
		if (chance == 0 || chance == 2 || chance == 3) {
			g.y[‘isY‘] = 1;
		} else {
			g.y[‘isY‘] = 0;
		}
		g.y[‘time‘] = 0;
	}

	// 创建一个新的图形
	function createNewGraph() {
		var n; 
		do {
			n = Math.ceil(Math.random() * 7);
		} while (n == g.color);
		createGraph();
		g.color = n;
		drawNowerGraph();
	}

	createNewGraph();

	// 绘制当前控制的图形
	function drawNowerGraph() {
		var length = g.graphArray.length;
		var address;
		for (var i = length - 1; i >= 0; i--) {
			address = g.graphArray[i].x + ‘_‘ + g.graphArray[i].y;
			if (doc.getElementById(address).classList.contains(‘cell‘)) {
				location.reload();
			}
			doc.getElementById(address).classList.add(‘cell‘);
			doc.getElementById(address).classList.add(‘color‘ + g.color);
		}
	}

	// 擦除当前控制的图形
	function eraseNowerGraph() {
		var length = g.graphArray.length;
		var address;
		for (var i = length - 1; i >= 0; i--) {
			address = g.graphArray[i].x + ‘_‘ + g.graphArray[i].y;
			doc.getElementById(address).classList.remove(‘cell‘);
			doc.getElementById(address).classList.remove(‘color‘ + g.color);
		}
	}

	// 绘制已经固定的图形(除当前控制的图形以外的)
	function drawSavedGraph() {
		var length = g.savedArray.length,
			address;
		for (var i = length - 1; i >= 0; i--) {
			address = g.savedArray[i].x + ‘_‘ + g.savedArray[i].y;
			doc.getElementById(address).classList.add(‘cell‘);
			doc.getElementById(address).classList.add(‘color‘ + g.savedArray[i].color);
		}
	}

	// 擦除已经固定的图形
	function eraseSavedGraph() {
		var length = g.savedArray.length,
			address;
		for (var i = length - 1; i >= 0; i--) {
			address = g.savedArray[i].x + ‘_‘ + g.savedArray[i].y;
			doc.getElementById(address).classList.remove(‘cell‘);
			doc.getElementById(address).classList.remove(‘color‘ + g.savedArray[i].color);
		}
	}

	// 检测是否可以消行
	function checkCanDelete() {
		var info = {
				‘lines‘: [],
				‘top‘: 0
			},
			flagLines,  //判断能否消行的辅助标识
			flagTop;  //如果某一行g.tetrisArray[i][j]均为-1,即可退出循环
		for (var i = o.height - 1; i >= 0; i--) {
			flagLines = true;
			flagTop = 0;
			for (var j = o.width - 1; j >= 0; j--) {
				if (g.tetrisArray[i][j] == -1) {
					flagLines = false;
					flagTop++;
				}
			}
			if (flagLines) {
				info[‘lines‘].push(i);
			}
			if (flagTop == o.width) {
				info[‘top‘] = i;
				break;
			}
		}
		return info;
	}

	// 消行
	function deleteLines(info) {
		for (var l = info[‘lines‘].length -1; l >= 0; l--) {
			for (var i = info[‘lines‘][l]; i > info[‘top‘]; i--) {
				for (var j = o.width - 1; j >= 0; j--) {
					g.tetrisArray[i][j] = g.tetrisArray[i - 1][j];
				}
			}
		}
		eraseSavedGraph();
		g.savedArray.length = 0;
		for (var i = o.height - 1; i >= info[‘top‘] + info[‘lines‘].length; i--) {
			for (var j = o.width - 1; j >= 0; j--) {
				if (g.tetrisArray[i][j] != -1) {
					g.savedArray.push({
						x: i,
						y: j,
						color: g.tetrisArray[i][j]
					})
				}
			}
		}
		drawSavedGraph();
	}
	
	document.onkeydown = control;

	// 控制
	function control() {
		if (event.keyCode == 32) {
			if (g.start == 0) {
				g.timer = setInterval(down, 500);
				g.start = 1;
			} else {
				clearInterval(g.timer);
				g.start = 0;
			}
		}
		if (g.start == 0) {
			return false;
		}
		var dir = event.keyCode;
		switch(dir) {
			case 37:
				left();
				break;
			case 38:
				if (g.shape == 1) break;
				rotate(g.z);
				break;
			case 39:
				right();
				break;
			case 40:
				down();
				break;
		}
	}

	// 左移动
	function left() {
		eraseNowerGraph();
		var address,
			checkArray = mirror(g.graphArray),
			length = g.graphArray.length,
			can_move = true;
		for (var i = length - 1; i >= 0; i--) {
			address = checkArray[i].x + ‘_‘ + (checkArray[i].y - 1);
			if (--checkArray[i].y < 0 || doc.getElementById(address).classList.contains(‘cell‘)) {
				can_move = false;
			}
		}
		if (can_move == true) {
			g.graphArray = checkArray;
		}
		drawNowerGraph();
	}

	// 右移动
	function right() {
		eraseNowerGraph();
		var address,
			checkArray = mirror(g.graphArray),
			length = g.graphArray.length,
			can_move = true;
		for (var i = length - 1; i >= 0; i--) {
			address = checkArray[i].x + ‘_‘ + (checkArray[i].y + 1);
			if (++checkArray[i].y > o.width - 1 || doc.getElementById(address).classList.contains(‘cell‘)) {
				can_move = false;
			}
		}
		if (can_move == true) {
			g.graphArray = checkArray;
		}
		drawNowerGraph();
	}

	// 下移动
	function down() {
		eraseNowerGraph();
		var address,
			x,
			y,
			checkArray = mirror(g.graphArray),
			length = g.graphArray.length,
			can_move = true;
		for (var i = length - 1; i >= 0; i--) {
			address = (checkArray[i].x + 1) + ‘_‘ + checkArray[i].y;
			if (++checkArray[i].x > o.height - 1 || doc.getElementById(address).classList.contains(‘cell‘)) {
				can_move = false;
			}
		}
		if (can_move == true) {
			g.graphArray = checkArray;
		}
		drawNowerGraph();
		if (can_move == false) {
			for (var i = length - 1; i >= 0; i--) {
				x = g.graphArray[i].x;
				y = g.graphArray[i].y;
				g.savedArray.push({
					x: x,
					y: y,
					color: g.color
				})
				g.tetrisArray[x][y] = g.color;
			}
			var info = checkCanDelete();
			if (info[‘lines‘].length) {
				deleteLines(info);
			}
			createNewGraph();
		}
	}

	// 旋转  坐标(x,y)绕(x0,y0)逆时针旋转90度后的坐标为(x0+y0-y,y0-x0+x)
	// 一字型、Z字型和方块型采用此旋转算法会下移一格,对这3种图形做了处理
	function rotate() {
		eraseNowerGraph();
		var originX,
			originY,
			address,
			length = g.graphArray.length,
			can_rotate = true,
			checkArray = mirror(g.graphArray),
			centerX = Math.round((g.graphArray[0].x + g.graphArray[1].x + g.graphArray[2].x + g.graphArray[3].x) / 4),
			centerY = Math.round((g.graphArray[0].y + g.graphArray[1].y + g.graphArray[2].y + g.graphArray[3].y) / 4);
		for (var i = length - 1; i >= 0; i--) {
			originX = g.graphArray[i].x;
			originY = g.graphArray[i].y;
			checkArray[i].x = centerX + centerY - originY - g.y[‘time‘];
			checkArray[i].y = centerY - centerX + originX;
			address = checkArray[i].x + ‘_‘ + checkArray[i].y;
			if (checkArray[i].y < 0 || checkArray[i].y > o.width - 1 || checkArray[i].x < 0 || checkArray[i].x > o.height || doc.getElementById(address).classList.contains(‘cell‘)) {
				can_rotate = false;
			}
		}

		if (can_rotate == true) {
			g.graphArray = checkArray;
			if (g.y[‘isY‘] == 1) {
				g.y[‘time‘] = g.y[‘time‘] == 1 ? 0 : 1;
			}
		}
		drawNowerGraph();
	}

	// 创建一个保存graphArray数据的继承函数
	// 这个函数是为了解决包含引用类型值的属性始终都会共享相应的值。继承函数具体可见《JavaScript高级程序设计》P171。引用赋值可见https://www.zhihu.com/question/27114726。
	function mirror(graphArray) {
		var checkArray = [{x: 0, y: 0}, {x: 0, y: 0}, {x: 0, y: 0}, {x: 0, y: 0}];
		for (var i = 0; i < 4; i++) {
			checkArray[i].x = graphArray[i].x;
			checkArray[i].y = graphArray[i].y;
		}
		return checkArray;
	}
})();

</script>
</body>
</html>

  

我也来写一个俄罗斯方块