上周末写好了大部分功能函数,今天趁工作做完,把剩下的功能写好。下面先给出效果图:
代码如下:
<!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>
时间: 2024-10-05 12:10:37