参考自黑马教程
HTML及CSS初始化代码
<body>
<div class="score">本次得分:0</div>
<div class="speed">
<div id="currentSpeed">
当前速度:Min
</div>
<button>点击加速</button>
</div>
<div class="box">
<!-- <div class="block1"></div> -->
</div>
</body>
<style>
body {
background-color: #303952;
}
.score,
.speed {
color: ghostwhite;
text-align: center;
font-size: 20px;
/* margin-top: 30px; */
}
.speed{
display: flex;
justify-content: center;
}
.box {
width: 375px;
height: 650px;
background-color: #596275;
/* border: 1px solid #0abde3; */
box-shadow: #33d9b2 0px 0px 8px;
margin: 10px auto;
position: relative;
top: 0;
left: 0;
}
.block1 {
width: 25px;
height: 25px;
background-color: #33d9b2;
box-shadow: #33d9b2 0px 0px 8px;
position: absolute;
/* top: 0px; */
}
.fixedBlock {
width: 25px;
height: 25px;
background-color: #a5b1c2;
box-shadow: #a5b1c2 0px 0px 8px;
position: absolute;
}
button {
border: none;
height: 25px;
background: white;
color: #596275;
box-shadow: #a5b1c2 0px 0px 8px;
margin-left: 20px;
}
</style>
键盘控制元素移动—onKeyDown()
函数
function onKeyDown() {
var block1 = document.getElementsByClassName("block1")[0]
document.onkeydown = function (event) {
//上下左右 38 40 37 39
switch (event.keyCode) {
// 空格旋转方块
case 32:
rotate()
break
// 向右移动
case 39:
move(1, 0)
break;
// 向下移动
case 40:
move(0, 1)
break;
// 向左移动
case 37:
move(-1, 0)
break;
}
}
}
控制块元素移动—move()
函数
// 定义步长为单个块元素的边长 即25px
const STEP = 25
function move(x, y) {
var block1 = document.getElementsByClassName("block1")[0]
block1.style.top = parseInt(block1.style.top||0)+y*STEP+"px"
block1.style.left = parseInt(block1.style.left||0)+x*STEP+"px"
}
构建模型
将模型看作放在一个十六宫格中,每次块元素先在十六宫格中定位构成模型,然后十六宫格再在整个容器中定位,改变位置或旋转。
// 分割容器
const ROW = 26 // 26行
const COL = 15 // 15列
// 创建每个模型的数据源
var MODELS = [
// L
{
0: { row: 2, col: 0 }, 1: { row: 2, col: 1 },
2: { row: 2, col: 2 }, 3: { row: 1, col: 0 }
}
]
// 记录当前使用的模型
var curModel = {}
creatModel()
函数
function creatModel() {
// 确定模型
curModel = MODELS[0]
// 根据数据源生成对应块元素
for (var key in curModel) {
var divBox = document.createElement("div")
divBox.className = "block1"
document.querySelector(".box").appendChild(divBox)
}
locationModel()
}
根据数据源定位块元素—locationModel()
函数
function locationModel() {
// 获取模型
var eles = document.getElementsByClassName("block1")
// 遍历模型获取单个元素
for (let i = 0; i < eles.length; i++) {
// 单个块元素
var singleBlock = eles[i]
// 每个块元素对应的数据
var blockModel = curModel[i]
singleBlock.style.top = blockModel.row * STEP + "px"
singleBlock.style.left = blockModel.col * STEP + "px"
}
}
此时页面左上角出现一个L型模型 但用键盘控制移动时只能移动一个块元素 此时需要修改
move()
函数
控制模型移动—move()
函数
模型的移动实际上是十六宫格的移动
// 标记十六宫格的位置
var currentX = 0, currentY = 0;
function move(x, y) {
currentX += x
currentY += y
locationModel()
}
调整locationModel()
函数
function locationModel() {
// 获取模型
var eles = document.getElementsByClassName("block1")
// 遍历模型获取单个元素
for (let i = 0; i < eles.length; i++) {
// 单个块元素
var singleBlock = eles[i]
/* 每个块元素的位置
由十六宫格所在的位置和块元素在十六宫格的定位共同决定 */
var blockModel = curModel[i]
singleBlock.style.top = (currentY + blockModel.row) * STEP + "px"
singleBlock.style.left = (currentX + blockModel.col) * STEP + "px"
}
}
控制模型旋转—rotate()
函数
function rotate() {
/* 旋转后的行 = 旋转前的列
旋转后的列= 3 - 旋转前的行 */
for (var key in cloneCurModel) {
var blockModel = cloneCurModel[key]
var t = blockModel.row
blockModel.row = blockModel.col
blockModel.col = 3 - t
}
locationModel()
}
越界判断—checkBound()
函数
function checkBound() {
// 定义边界
const LeftBound = 0, RightBound = COL, BottomBound = ROW
for (let key in curModel) {
var blockModel = curModel[key]
// 如果越界
if ((blockModel.col + currentX) < LeftBound)
currentX++
if ((blockModel.col + currentX) >= RightBound)
currentX--
// 触碰底部
if ((blockModel.row + currentY) >= BottomBound) {
currentY--
fixedModel()
}
}
}
最后在locationModel()
函数中调用
固定块元素—fixedModel()
函数
function fixedModel() {
var eles = document.getElementsByClassName("block1")
for (let i = 0; i < 4; i++) {
var singleBlock = eles[0]
singleBlock.className = "fixedBlock"
}
creatModel()
}
注意:在遍历时不能用
let i = 0; i < eles.length; i++ var singleBlock = eles[i]
因为前面已经遍历过的元素改变了类名导致eles.length
改变,这样遍历会漏掉模型中的元素
最后需要在creatModel()
中加入初始化模型位置的代码
currentX = 0
currentY = 0
判断块元素与块元素之间是否接触—isMeet()
函数
要判断块元素与块元素之间是否接触就要先记录下所有块元素的位置
var fixedBlocks = {}
function fixedModel() {
var eles = document.getElementsByClassName("block1")
for (let i = 0; i < 4; i++) {
var singleBlock = eles[0] //activityModelEle
singleBlock.className = "fixedBlock"
// 记录块元素
var blockModel = curModel[i]
fixedBlocks[(currentY + blockModel.row) + "_" + (currentX + blockModel.col)] = singleBlock
}
creatModel()
}
function isMeet(x, y, model) {
// (x,y) 模型将要移动到的位置
// 模型将要发生的变化 如旋转等
for (var key in model) {
var blockModel = model[key]
if (fixedBlocks[(y + blockModel.row) + "_" + (x + blockModel.col)]) {
return true
}
}
return false++
}
在旋转时判断接触
<script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
function rotate() {
var cloneCurModel = _.cloneDeep(curModel)
/* 旋转后的行 = 旋转前的列
旋转后的列= 3 - 旋转前的行 */
for (var key in cloneCurModel) {
var blockModel = cloneCurModel[key]
var t = blockModel.row
blockModel.row = blockModel.col
blockModel.col = 3 - t
}
if (isMeet(currentX, currentY, cloneCurModel)) {
return
}
curModel = cloneCurModel
locationModel()
}
判断一行是否被铺满—isRemoveLine()
function isRemoveLine() {
/* 判断某行中,每一列是否都存在块元素,是则清理 */
/* 遍历所有行所有列 */
for (var i = 0; i < ROW; i++) {
/* 假设当前行铺满,设flag=true */
var flag = true;
for (var j = 0; j < COL; j++) {
/* 当前行没铺满 */
if (!fixedBlocks[i + "_" + j]) {
flag = false;
break;
}
}
/* 当某行铺满,flag==true */
if (flag) {
removeLine(i)
}
}
}
在fixedModel()
函数中调用
清理被铺满的行—removeLine()
function removeLine(line) {
cnt++
for (let i = 0; i < COL; i++) {
// 删除块元素及其数据源
document.querySelector(".box").removeChild(fixedBlocks[line + "_" + i])
fixedBlocks[line + "_" + i] = null
}
downLine(line)
}
下落—downLine()
function downLine(line) {
for (let i = line - 1; i >= 0; i--) {
for (let j = 0; j < COL; j++) {
if (!fixedBlocks[i + "_" + j])
continue
// 修改在数据源中的位置
fixedBlocks[(i + 1) + "_" + j] = fixedBlocks[i + "_" + j]
// 在容器中下落
fixedBlocks[(i + 1) + "_" + j].style.top = (i + 1) * STEP + "px"
fixedBlocks[i + "_" + j] = null
}
}
var score = document.querySelector(".score")
// cnt+=1;
score.innerHTML = "本次得分:" + (cnt + 1) + "0"
}
自动下落—autoDown()
function autoDown() {
var but = document.getElementsByTagName("button")[0]
but.onclick = function () {
if (speed < 300) {
but.disabled = "disabled"
but.style.backgroundColor = "#a4b0be"
return
}
speed -= 300
var currentSpeed = document.getElementById("currentSpeed")
if(speed==500)
currentSpeed.innerHTML = "当前速度:Mid"
if(speed==200)
currentSpeed.innerHTML = "当前速度:Max"
clearInterval(timer)
timer = setInterval(function () {
move(0, 1)
}, speed)
}
if (timer)
clearInterval(timer)
timer = setInterval(function () {
move(0, 1)
}, speed)
}
游戏结束判断
function isGameOver() {
for (let i = 0; i < COL; i++) {
if (fixedBlocks["0_" + i])
return true
}
return false
}
function over() {
if (timer)
clearInterval(timer)
var score = document.querySelector(".score")
alert("你好菜啊\n" + score.innerHTML)
}