200px
,高爲 360px
的背景容器<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>俄羅斯方塊</title> <style> .container { position: relative; width: 200px; height: 360px; background-color: #000; } </style> </head> <body> <!-- 背景容器 --> <div class="container"></div> </body> </html>
20 * 20
的塊元素<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>俄羅斯方塊</title> <style> .container { position: relative; width: 200px; height: 360px; background-color: #000; } .activity-model { width: 20px; height: 20px; background-color: cadetblue; border: 1px solid #eeeeee; box-sizing: border-box; position: absolute; } </style> </head> <body> <!-- 背景容器 --> <div class="container"> <!-- 塊元素 --> <div class="activity-model"></div> </div> </body> </html>
20px
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>俄羅斯方塊</title> <style> .container { position: relative; width: 200px; height: 360px; background-color: #000; } .activity-model { width: 20px; height: 20px; background-color: cadetblue; border: 1px solid #eeeeee; box-sizing: border-box; position: absolute; } </style> </head> <body> <!-- 背景容器 --> <div class="container"> <!-- 塊元素 --> <div class="activity-model"></div> </div> <script> // 常量 // 每次移動的距離 步長 const STEP = 20 init() // 入口方法 function init() { onKeyDown() } // 監聽用戶的鍵盤事件 function onKeyDown() { document.onkeydown = event => { switch (event.keyCode) { case 38: // 上 move(0, -1) break; case 39: // 右 move(1, 0) break; case 40: // 下 move(0, 1) break; case 37: // 左 move(-1, 0) break; default: break; } } } // 移動 function move(x, y) { // 控制塊元素進行移動 const activityModelEle = document.getElementsByClassName("activity-model")[0] activityModelEle.style.top = parseInt(activityModelEle.style.top || 0) + y * STEP + "px" activityModelEle.style.left = parseInt(activityModelEle.style.left || 0) + x * STEP + "px" } </script> </body> </html>
L
形狀的模型18
行,10
列。行高,列高均爲20
// 常量 // 每次移動的距離 步長 const STEP = 20 // 分割容器 // 18行 10列 const ROW_COUNT = 18, COL_COUNT = 10
16宮格
爲基準,定義 L
形狀的 4
個方塊的位置// 分割容器 // 18行 10列 const ROW_COUNT = 18, COL_COUNT = 10 // 建立每一個模型的數據源 const MODELS = [ // 第1個模型數據源(L型) { 0: { row: 2, col: 0 }, 1: { row: 2, col: 1 }, 2: { row: 2, col: 2 }, 3: { row: 1, col: 2 } }]
L
型模型,根據 16
宮格中的數據將模型渲染到頁面上// 分割容器 // 18行 10列 const ROW_COUNT = 18, COL_COUNT = 10 // 建立每一個模型的數據源 const MODELS = [ // 第1個模型數據源(L型) { 0: { row: 2, col: 0 }, 1: { row: 2, col: 1 }, 2: { row: 2, col: 2 }, 3: { row: 1, col: 2 } }] // 變量 // 當前使用的模型 let currentModel = {} init() // 入口方法 function init() { createModel() onKeyDown() } // 根據模型使用的數據建立對應的塊元素 function createModel() { // 肯定當前使用哪個模型 currentModel = MODELS[0] // 生成對應數量的塊元素 for (const key in currentModel) { const divEle = document.createElement('div') divEle.className = "activity-model" document.getElementById("container").appendChild(divEle) } // 定位塊元素位置 locationBlocks() } // 根據數據源定位塊元素的位置 function locationBlocks() { // 1 拿到全部的塊元素 const eles = document.getElementsByClassName("activity-model") for (let i = 0; i < eles.length; i++) { // 單個塊元素 const activityModelEle = eles[i] // 2 找到每一個塊元素對應的數據 (行、列) const blockModel = currentModel[i] // 3 根據每一個塊元素對應的數據來指定塊元素的位置 activityModelEle.style.top = blockModel.row * STEP + "px" activityModelEle.style.left = blockModel.col * STEP + "px" } }
16 宮格 進行移動
// 根據數據源定位塊元素的位置 function locationBlocks() { // 1 拿到全部的塊元素 const eles = document.getElementsByClassName("activity-model") for (let i = 0; i < eles.length; i++) { // 單個塊元素 const activityModelEle = eles[i] // 2 找到每一個塊元素對應的數據 (行、列) const blockModel = currentModel[i] // 3 根據每一個塊元素對應的數據來指定塊元素的位置 // 每一個塊元素的位置由2個值肯定: // a. 16 宮格所在的位置 // b. 塊元素在 16 宮格中的位置 activityModelEle.style.top = (currentY + blockModel.row) * STEP + "px" activityModelEle.style.left = (currentX + blockModel.col) * STEP + "px" } } // 移動 function move(x, y) { // 控制16宮格元素進行移動 currentX += x currentY += y // 根據16宮格的位置來從新定位塊元素 locationBlocks() }
移動後的行 = 移動前的列
移動後的列 = 3 - 移動前的行
// 監聽用戶的鍵盤事件 function onKeyDown() { document.onkeydown = event => { switch (event.keyCode) { case 38: // 上 // move(0, -1) rotate() break; case 39: // 右 move(1, 0) break; case 40: // 下 move(0, 1) break; case 37: // 左 move(-1, 0) break; default: break; } } } // 旋轉模型 function rotate() { // 算法 // 旋轉後的行 = 旋轉前的列 // 旋轉後的列 = 3 - 旋轉前的行 // 遍歷模型數據源 for (const key in currentModel) { // 塊元素的數據 const blockModel = currentModel[key] // 實現算法 let temp = blockModel.row blockModel.row = blockModel.col blockModel.col = 3 - temp } locationBlocks() }
// 根據數據源定位塊元素的位置 function locationBlocks() { // 判斷一下塊元素的越界行爲 checkBound() // 1 拿到全部的塊元素 const eles = document.getElementsByClassName("activity-model") for (let i = 0; i < eles.length; i++) { // 單個塊元素 const activityModelEle = eles[i] // 2 找到每一個塊元素對應的數據 (行、列) const blockModel = currentModel[i] // 3 根據每一個塊元素對應的數據來指定塊元素的位置 // 每一個塊元素的位置由2個值肯定: // a. 16 宮格所在的位置 // b. 塊元素在 16 宮格中的位置 activityModelEle.style.top = (currentY + blockModel.row) * STEP + "px" activityModelEle.style.left = (currentX + blockModel.col) * STEP + "px" } } // 控制模型只能在容器中 function checkBound() { // 定義模型能夠活動的邊界 let leftBound = 0, rightBound = COL_COUNT, bottomBound = ROW_COUNT // 當塊元素超出了邊界以後,讓 16 宮格後退1格 for (const key in currentModel) { // 塊元素的數據 const blockModel = currentModel[key] // 左側越界 if ((blockModel.col + currentX) < 0) { currentX++ } // 右側越界 if ((blockModel.col + currentX) >= rightBound) { currentX-- } // 底部越界 if ((blockModel.row + currentY) >= bottomBound) { currentY-- } } }
灰色
固定在底部,同時生成一個新的模型.fixed-model { width: 20px; height: 20px; background-color: #fefefe; border: 1px solid #333333; box-sizing: border-box; position: absolute; }
// 根據模型使用的數據建立對應的塊元素 function createModel() { // 肯定當前使用哪個模型 currentModel = MODELS[0] // 重置16宮格的位置 currentY = 0 currentY = 0 // 生成對應數量的塊元素 for (const key in currentModel) { const divEle = document.createElement('div') divEle.className = "activity-model" document.getElementById("container").appendChild(divEle) } // 定位塊元素位置 locationBlocks() } // 控制模型只能在容器中 function checkBound() { // 定義模型能夠活動的邊界 let leftBound = 0, rightBound = COL_COUNT, bottomBound = ROW_COUNT // 當塊元素超出了邊界以後,讓 16 宮格後退1格 for (const key in currentModel) { // 塊元素的數據 const blockModel = currentModel[key] // 左側越界 if ((blockModel.col + currentX) < 0) { currentX++ } // 右側越界 if ((blockModel.col + currentX) >= rightBound) { currentX-- } // 底部越界 if ((blockModel.row + currentY) >= bottomBound) { currentY-- fixedBottomModel() // 把模型固定在底部 } } } // 把模型固定在底部 function fixedBottomModel() { // 1 改變模型的樣式 // 2 禁止模型再進行移動 const activityModelEles = document.getElementsByClassName('activity-model') ;[...activityModelEles].forEach((ele, i) => { // 更改塊元素類名 ele.className = "fixed-model" // 把該塊元素放入變量中 const blockModel = currentModel[i] fixedBlocks[`${currentY + blockModel.row}_${currentX + blockModel.col}`] = ele }) // 3 建立新的模型 createModel() }
左右接觸
和底部接觸
// 記錄全部塊元素的位置 // key=行_列 : V=塊元素 const fixedBlocks = {}
fixedBlocks
中// 把模型固定在底部 function fixedBottomModel() { // 1 改變模型的樣式 // 2 禁止模型再進行移動 const activityModelEles = document.getElementsByClassName('activity-model') ;[...activityModelEles].forEach((ele, i) => { // 更改塊元素類名 ele.className = "fixed-model" // 把該塊元素放入變量中 const blockModel = currentModel[i] fixedBlocks[`${currentY + blockModel.row}_${currentX + blockModel.col}`] = ele }) // 3 建立新的模型 createModel() }
// 移動 function move(x, y) { // 16宮格移動 if (isMeet(currentX + x, currentY + y, currentModel)) { return } currentX += x currentY += y // 根據16宮格的位置來從新定位塊元素 locationBlocks() } // 旋轉模型 function rotate() { // 算法 // 旋轉後的行 = 旋轉前的列 // 旋轉後的列 = 3 - 旋轉前的行 // 克隆一下 currentModel 深拷貝 const cloneCurrentModel = JSON.parse(JSON.stringify(currentModel)) // 遍歷模型數據源 for (const key in cloneCurrentModel) { // 塊元素的數據 const blockModel = cloneCurrentModel[key] // 實現算法 let temp = blockModel.row blockModel.row = blockModel.col blockModel.col = 3 - temp } // 若是旋轉以後會發生觸碰,那麼就不須要進行旋轉了 if (isMeet(currentX, currentY, cloneCurrentModel)) { return } // 接受了此次旋轉 currentModel = cloneCurrentModel locationBlocks() } // 判斷模型之間的觸碰問題 // x, y 表示16宮格《將要》移動的位置 // model 表示當前模型數據源《將要》完成的變化 function isMeet(x, y, model) { // 所謂模型之間的觸碰,在一個固定的位置已經存在一個被固定的塊元素時,那麼 // 活動中的模型不能夠再佔用該位置 // 判斷觸碰,就是在判斷活動中的模型《將要移動到的位置》是否已經存在被固定 // 的塊元素 // 返回 true 表示將要移動到的位置會發生觸碰 不然返回 false for (const key in model) { const blockModel = model[key] // 該位置是否已經存在塊元素? if (fixedBlocks[`${y + blockModel.row}_${x + blockModel.col}`]) { return true } } return false }
// 移動 function move(x, y) { if (isMeet(currentX + x, currentY + y, currentModel)) { // 底部的觸碰發生在移動16宮格的時候,而且這次移動是由於 Y 軸的變化而引發的 if (y != 0) { // 模型之間發生觸碰了 fixedBottomModel() } return } // 控制16宮格元素進行移動 currentX += x currentY += y // 根據16宮格的位置來從新定位塊元素 locationBlocks() }
// 把模型固定在底部 function fixedBottomModel() { // 1 改變模型的樣式 // 2 禁止模型再進行移動 const activityModelEles = document.getElementsByClassName('activity-model') ;[...activityModelEles].forEach((ele, i) => { ele.className = "fixed-model" // 把該塊元素放入變量中 const blockModel = currentModel[i] fixedBlocks[`${currentY + blockModel.row}_${currentX + blockModel.col}`] = ele }) // 判斷某一行是否要清理 isRemoveLine() // 3 建立新的模型 createModel() } // 判斷一行是否被鋪滿 function isRemoveLine() { // 在一行中,每一列都存在塊元素,那麼該行就須要被清理了 // 遍歷全部行中的全部列 // 遍歷全部行 for (let i = 0; i < ROW_COUNT; i++) { // 標記符 假設當前行已經被鋪滿了 let flag = true // 遍歷當前行中的全部列 for (let j = 0; j < COL_COUNT; j++) { // 若是當前行中有一列沒有數據,那麼就說明當前行沒有被鋪滿 if (!fixedBlocks[`${i}_${j}`]) { flag = false break } } if (flag) { // 該行已經被鋪滿了 console.log("該行已經被鋪滿了") } } }
function isRemoveLine() { // 在一行中,每一列都存在塊元素,那麼該行就須要被清理了 // 遍歷全部行中的全部列 // 遍歷全部行 for (let i = 0; i < ROW_COUNT; i++) { // 標記符 假設當前行已經被鋪滿了 let flag = true // 遍歷當前行中的全部列 for (let j = 0; j < COL_COUNT; j++) { // 若是當前行中有一列沒有數據,那麼就說明當前行沒有被鋪滿 if (!fixedBlocks[`${i}_${j}`]) { flag = false break } } if (flag) { // 該行已經被鋪滿了 removeLine(i) } } } // 清理被鋪滿的這一行 function removeLine(line) { // 1 刪除該行中全部的塊元素 // 2 刪除該行全部塊元素的數據源 // 遍歷該行中的全部列 for (let i = 0; i < COL_COUNT; i++) { // 1 刪除該行中全部的塊元素 document.getElementById("container").removeChild(fixedBlocks[`${line}_${i}`]) // 2 刪除該行全部塊元素的數據源 fixedBlocks[`${line}_${i}`] = null } }
// 清理被鋪滿的這一行 function removeLine(line) { // 1 刪除該行中全部的塊元素 // 2 刪除該行全部塊元素的數據源 // 遍歷該行中的全部列 for (let i = 0; i < COL_COUNT; i++) { // 1 刪除該行中全部的塊元素 document.getElementById("container").removeChild(fixedBlocks[`${line}_${i}`]) // 2 刪除該行全部塊元素的數據源 fixedBlocks[`${line}_${i}`] = null } downLine(line) } // 讓被清理行之上的塊元素下落 function downLine(line) { // 1 被清理行之上的全部塊元素數據源所在行數 + 1 // 2 讓塊元素在容器中的位置下落 // 3 清理以前的塊元素 // 遍歷被清理行之上的全部行 for (let i = line - 1; i >= 0; i--) { // 該行中的全部列 for (let j = 0; j < COL_COUNT; j++) { if (!fixedBlocks[`${i}_${j}`]) continue // 存在數據 // 1 被清理行之上的全部塊元素數據源所在行數 + 1 fixedBlocks[`${i + 1}_${j}`] = fixedBlocks[`${i}_${j}`] // 2 讓塊元素在容器中的位置下落 fixedBlocks[`${i + 1}_${j}`].style.top = (i + 1) * STEP + "px" // 3 清理以前的塊元素 fixedBlocks[`${i}_${j}`] = null } } }
// 建立每一個模型的數據源 const MODELS = [ // 第1個模型數據源(L型) { 0: { row: 2, col: 0 }, 1: { row: 2, col: 1 }, 2: { row: 2, col: 2 }, 3: { row: 1, col: 2 } }, // 第2個模型數據源(凸) { 0: { row: 1, col: 1 }, 1: { row: 0, col: 0 }, 2: { row: 1, col: 0 }, 3: { row: 2, col: 0 } }, // 第3個模型數據源(田) { 0: { row: 1, col: 1 }, 1: { row: 2, col: 1 }, 2: { row: 1, col: 2 }, 3: { row: 2, col: 2 } }, // 第4個模型數據源(一) { 0: { row: 0, col: 0 }, 1: { row: 0, col: 1 }, 2: { row: 0, col: 2 }, 3: { row: 0, col: 3 } }, // 第5個模型數據源(Z) { 0: { row: 1, col: 1 }, 1: { row: 1, col: 2 }, 2: { row: 2, col: 2 }, 3: { row: 2, col: 3 } } ]
// 根據模型使用的數據建立對應的塊元素 function createModel() { // 肯定當前使用哪個模型 const randow = Math.floor(Math.random() * MODELS.length) // 取0~4之間的隨機數 currentModel = MODELS[randow] // 重置16宮格的位置 currentY = 0 currentY = 0 // 生成對應數量的塊元素 for (const key in currentModel) { const divEle = document.createElement('div') divEle.className = "activity-model" document.getElementById("container").appendChild(divEle) } // 定位塊元素位置 locationBlocks() }
// 定時器 let mInterval = null // 根據模型使用的數據建立對應的塊元素 function createModel() { // 肯定當前使用哪個模型 // 肯定當前使用哪個模型 const randow = Math.floor(Math.random() * MODELS.length) // 取0~4之間的隨機數 currentModel = MODELS[randow] // 重置16宮格的位置 currentY = 0 currentY = 0 // 生成對應數量的塊元素 for (const key in currentModel) { const divEle = document.createElement('div') divEle.className = "activity-model" document.getElementById("container").appendChild(divEle) } // 定位塊元素位置 locationBlocks() // 模型自動下落 autoDown() } // 讓模型自動下落 function autoDown() { if (mInterval) { clearInterval(mInterval) } mInterval = setInterval(() => { move(0, 1) }, 600) }
// 根據模型使用的數據建立對應的塊元素 function createModel() { // 判斷遊戲是否結束 if (isGameOver()) { console.log("遊戲結束!") return } // 肯定當前使用哪個模型 const randow = Math.floor(Math.random() * MODELS.length) // 取0~4之間的隨機數 currentModel = MODELS[randow] // 重置16宮格的位置 currentY = 0 currentY = 0 // 生成對應數量的塊元素 for (const key in currentModel) { const divEle = document.createElement('div') divEle.className = "activity-model" document.getElementById("container").appendChild(divEle) } // 定位塊元素位置 locationBlocks() // 模型自動下落 autoDown() } // 判斷遊戲結束 function isGameOver() { // 當第0行存在塊元素的時候,表示遊戲結束了 for (let i = 0; i < COL_COUNT; i++) { if (fixedBlocks[`0_${i}`]) return true } return false }
// 根據模型使用的數據建立對應的塊元素 function createModel() { // 判斷遊戲是否結束 if (isGameOver()) { gameOver() // 結束遊戲 return } // 肯定當前使用哪個模型 const randow = Math.floor(Math.random() * MODELS.length) // 取0~4之間的隨機數 currentModel = MODELS[randow] // 重置16宮格的位置 currentY = 0 currentY = 0 // 生成對應數量的塊元素 for (const key in currentModel) { const divEle = document.createElement('div') divEle.className = "activity-model" document.getElementById("container").appendChild(divEle) } // 定位塊元素位置 locationBlocks() // 模型自動下落 autoDown() } // 結束掉遊戲 function gameOver() { // 1 中止定時器 if (mInterval) { clearInterval(mInterval) } // 2 彈出對話框 alert("大吉大利,今晚吃雞!") }
body { display: flex; } #scores { margin-left: 20px; }
<!-- 背景容器 --> <div id="container" class="container"> <!-- 塊元素 --> <!-- <div class="activity-model"></div> --> </div> <div id="scores"> <p>最高分:<span id="max-score">0</span></p> <p>分數:<span id="current-score">0</span></p> <button onclick="reset()">從新開始</button> </div>
// 最高分 let maxScore = 0 // 當前分數 let score = 0 // 清理被鋪滿的這一行 function removeLine(line) { // 1 刪除該行中全部的塊元素 // 2 刪除該行全部塊元素的數據源 // 遍歷該行中的全部列 for (let i = 0; i < COL_COUNT; i++) { // 1 刪除該行中全部的塊元素 document.getElementById("container").removeChild(fixedBlocks[`${line}_${i}`]) // 2 刪除該行全部塊元素的數據源 fixedBlocks[`${line}_${i}`] = null } // 更新當前分數 score += COL_COUNT document.getElementById("current-score").innerHTML = score downLine(line) } // 結束掉遊戲 function gameOver() { // 1 中止定時器 if (mInterval) { clearInterval(mInterval) } // 重置最高分數 maxScore = Math.max(maxScore, score) document.getElementById("max-score").innerHTML = maxScore // 2 彈出對話框 alert("大吉大利,今晚吃雞!") } // 從新開始 function reset() { const container = document.getElementById("container") const childs = container.childNodes; for (let i = childs.length - 1; i >= 0; i--) { container.removeChild(childs[i]); } fixedBlocks = {} score = 0 document.getElementById("current-score").innerHTML = score init() }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>俄羅斯方塊</title> <style> body { display: flex; } .container { position: relative; width: 200px; height: 360px; background-color: #000; } .activity-model { width: 20px; height: 20px; background-color: cadetblue; border: 1px solid #eeeeee; box-sizing: border-box; position: absolute; } .fixed-model { width: 20px; height: 20px; background-color: #fefefe; border: 1px solid #333333; box-sizing: border-box; position: absolute; } #scores { margin-left: 20px; } </style> </head> <body> <!-- 背景容器 --> <div id="container" class="container"> <!-- 塊元素 --> <!-- <div class="activity-model"></div> --> </div> <div id="scores"> <p>最高分:<span id="max-score">0</span></p> <p>分數:<span id="current-score">0</span></p> <button onclick="reset()">從新開始</button> </div> <script> // 常量 // 每次移動的距離 步長 const STEP = 20 // 分割容器 // 18行 10列 const ROW_COUNT = 18, COL_COUNT = 10 // 建立每一個模型的數據源 const MODELS = [ // 第1個模型數據源(L型) { 0: { row: 2, col: 0 }, 1: { row: 2, col: 1 }, 2: { row: 2, col: 2 }, 3: { row: 1, col: 2 } }, // 第2個模型數據源(凸) { 0: { row: 1, col: 1 }, 1: { row: 0, col: 0 }, 2: { row: 1, col: 0 }, 3: { row: 2, col: 0 } }, // 第3個模型數據源(田) { 0: { row: 1, col: 1 }, 1: { row: 2, col: 1 }, 2: { row: 1, col: 2 }, 3: { row: 2, col: 2 } }, // 第4個模型數據源(一) { 0: { row: 0, col: 0 }, 1: { row: 0, col: 1 }, 2: { row: 0, col: 2 }, 3: { row: 0, col: 3 } }, // 第5個模型數據源(Z) { 0: { row: 1, col: 1 }, 1: { row: 1, col: 2 }, 2: { row: 2, col: 2 }, 3: { row: 2, col: 3 } } ] // 變量 // 當前使用的模型 let currentModel = {} // 標記16宮格的位置 let currentX = 0, currentY = 0 // 記錄全部塊元素的位置 // key=行_列 : V=塊元素 let fixedBlocks = {} // 定時器 let mInterval = null // 最高分 let maxScore = 0 // 當前分數 let score = 0 // 入口方法 function init() { createModel() onKeyDown() } init() // 根據模型使用的數據建立對應的塊元素 function createModel() { // 判斷遊戲是否結束 if (isGameOver()) { gameOver() return } // 肯定當前使用哪個模型 const randow = Math.floor(Math.random() * MODELS.length) // 取0~4之間的隨機數 currentModel = MODELS[randow] // 重置16宮格的位置 currentY = 0 currentY = 0 // 生成對應數量的塊元素 for (const key in currentModel) { const divEle = document.createElement('div') divEle.className = "activity-model" document.getElementById("container").appendChild(divEle) } // 定位塊元素位置 locationBlocks() // 模型自動下落 autoDown() } // 根據數據源定位塊元素的位置 function locationBlocks() { // 判斷一些塊元素的越界行爲 checkBound() // 1 拿到全部的塊元素 const eles = document.getElementsByClassName("activity-model") for (let i = 0; i < eles.length; i++) { // 單個塊元素 const activityModelEle = eles[i] // 2 找到每一個塊元素對應的數據 (行、列) const blockModel = currentModel[i] // 3 根據每一個塊元素對應的數據來指定塊元素的位置 // 每一個塊元素的位置由2個值肯定: // 1 16 宮格所在的位置 // 2 塊元素在 16 宮格中的位置 activityModelEle.style.top = (currentY + blockModel.row) * STEP + "px" activityModelEle.style.left = (currentX + blockModel.col) * STEP + "px" } } // 監聽用戶鍵盤事件 function onKeyDown() { document.onkeydown = event => { switch (event.keyCode) { case 38: // move(0, -1) rotate() break; case 39: move(1, 0) break; case 40: move(0, 1) break; case 37: move(-1, 0) break; default: break; } } } // 移動 function move(x, y) { // 控制塊元素進行移動 // const activityModelEle = document.getElementsByClassName("activity-model")[0] // activityModelEle.style.top = parseInt(activityModelEle.style.top || 0) + y * STEP + "px" // activityModelEle.style.left = parseInt(activityModelEle.style.left || 0) + x * STEP + "px" // 16宮格移動 if (isMeet(currentX + x, currentY + y, currentModel)) { // 底部的觸碰發生在移動16宮格的時候,而且這次移動是由於 Y 軸的變化而引發的 if (y != 0) { // 模型之間發生觸碰了 fixedBottomModel() } return } currentX += x currentY += y // 根據16宮格的位置來從新定位塊元素 locationBlocks() } // 旋轉模型 function rotate() { // 算法 // 旋轉後的行 = 旋轉前的列 // 旋轉後的列 = 3 - 旋轉前的行 // 克隆一下 currentModel 深拷貝 const cloneCurrentModel = JSON.parse(JSON.stringify(currentModel)) // 遍歷模型數據源 for (const key in cloneCurrentModel) { // 塊元素的數據 const blockModel = cloneCurrentModel[key] // 實現算法 let temp = blockModel.row blockModel.row = blockModel.col blockModel.col = 3 - temp } // 若是旋轉以後會發生觸碰,那麼就不須要進行旋轉了 if (isMeet(currentX, currentY, cloneCurrentModel)) { return } // 接受了此次旋轉 currentModel = cloneCurrentModel locationBlocks() } // 控制模型只能在容器中 function checkBound() { // 定義模型能夠活動的邊界 let leftBound = 0, rightBound = COL_COUNT, bottomBound = ROW_COUNT // 當塊元素超出了邊界以後,讓 16 宮格後退1格 for (const key in currentModel) { // 塊元素的數據 const blockModel = currentModel[key] // 左側越界 if ((blockModel.col + currentX) < 0) { currentX++ } // 右側越界 if ((blockModel.col + currentX) >= rightBound) { currentX-- } // 下側越界 if ((blockModel.row + currentY) >= bottomBound) { currentY-- fixedBottomModel() } } } // 把模型固定在底部 function fixedBottomModel() { // 1 改變模型的樣式 // 2 禁止模型再進行移動 const activityModelEles = document.getElementsByClassName('activity-model') ;[...activityModelEles].forEach((ele, i) => { ele.className = "fixed-model" // 把該塊元素放入變量中 const blockModel = currentModel[i] fixedBlocks[`${currentY + blockModel.row}_${currentX + blockModel.col}`] = ele }) // for (let i = activityModelEles.length - 1; i >= 0; i--) { // // 拿到每一個塊元素 // const activityModelEle = activityModelEles[i] // // 更改塊元素的類名 // activityModelEle.className = "fixed-model" // } // 判斷某一行是否要清理 isRemoveLine() // 3 建立新的模型 createModel() } // 判斷模型之間的觸碰問題 // x, y 表示16宮格《將要》移動的位置 // model 表示當前模型數據源《將要》完成的變化 function isMeet(x, y, model) { // 所謂模型之間的觸碰,在一個固定的位置已經存在一個被固定的塊元素時,那麼 // 活動中的模型不能夠再佔用該位置 // 判斷觸碰,就是在判斷活動中的模型《將要移動到的位置》是否已經存在被固定 // 的塊元素 // 返回 true 表示將要移動到的位置會發生觸碰 不然返回 false for (const key in model) { const blockModel = model[key] // 該位置是否已經存在塊元素? if (fixedBlocks[`${y + blockModel.row}_${x + blockModel.col}`]) { return true } } return false } // 判斷一行是否被鋪滿 function isRemoveLine() { // 在一行中,每一列都存在塊元素,那麼該行就須要被清理了 // 遍歷全部行中的全部列 // 遍歷全部行 for (let i = 0; i < ROW_COUNT; i++) { // 標記符 假設當前行已經被鋪滿了 let flag = true // 遍歷當前行中的全部列 for (let j = 0; j < COL_COUNT; j++) { // 若是當前行中有一列沒有數據,那麼就說明當前行沒有被鋪滿 if (!fixedBlocks[`${i}_${j}`]) { flag = false break } } if (flag) { // 該行已經被鋪滿了 removeLine(i) } } } // 清理被鋪滿的這一行 function removeLine(line) { // 1 刪除該行中全部的塊元素 // 2 刪除該行全部塊元素的數據源 // 遍歷該行中的全部列 for (let i = 0; i < COL_COUNT; i++) { // 1 刪除該行中全部的塊元素 document.getElementById("container").removeChild(fixedBlocks[`${line}_${i}`]) // 2 刪除該行全部塊元素的數據源 fixedBlocks[`${line}_${i}`] = null } // 更新當前分數 score += COL_COUNT document.getElementById("current-score").innerHTML = score downLine(line) } // 讓被清理行之上的塊元素下落 function downLine(line) { // 1 被清理行之上的全部塊元素數據源所在行數 + 1 // 2 讓塊元素在容器中的位置下落 // 3 清理以前的塊元素 // 遍歷被清理行之上的全部行 for (let i = line - 1; i >= 0; i--) { // 該行中的全部列 for (let j = 0; j < COL_COUNT; j++) { if (!fixedBlocks[`${i}_${j}`]) continue // 存在數據 // 1 被清理行之上的全部塊元素數據源所在行數 + 1 fixedBlocks[`${i + 1}_${j}`] = fixedBlocks[`${i}_${j}`] // 2 讓塊元素在容器中的位置下落 fixedBlocks[`${i + 1}_${j}`].style.top = (i + 1) * STEP + "px" // 3 清理以前的塊元素 fixedBlocks[`${i}_${j}`] = null } } } // 讓模型自動下落 function autoDown() { if (mInterval) { clearInterval(mInterval) } mInterval = setInterval(() => { move(0, 1) }, 600) } // 判斷遊戲結束 function isGameOver() { // 當第0行存在塊元素的時候,表示遊戲結束了 for (let i = 0; i < COL_COUNT; i++) { if (fixedBlocks[`0_${i}`]) return true } return false } // 結束掉遊戲 function gameOver() { // 1 中止定時器 if (mInterval) { clearInterval(mInterval) } // 重置最高分數 maxScore = Math.max(maxScore, score) document.getElementById("max-score").innerHTML = maxScore // 2 彈出對話框 alert("大吉大利,今晚吃雞!") } // 從新開始 function reset() { const container = document.getElementById("container") const childs = container.childNodes; for (let i = childs.length - 1; i >= 0; i--) { container.removeChild(childs[i]); } fixedBlocks = {} score = 0 document.getElementById("current-score").innerHTML = score init() } </script> </body> </html>
轉載於:https://blog.csdn.net/wanghuan1020/article/details/111473709css