古之立大事者,不惟有超世之才,亦必有堅忍不拔之志,——蘇軾
案例效果
原生 JavaScript 實作的 別踩方塊小游戲案例效果如下所示:

案例分析
靜態頁面
將這個靜態頁面可以劃分為四個部分,如下圖所示:

JavaScript 行為
-
黑塊的行為
-
生成新的黑塊
在視覺效果的以外生成一個新的黑塊,這樣顯得不是很怪
-
黑塊下落
在定時器中將黑塊的 top 值次增,移動到視覺效果外將其通過
Element.remove()方法洗掉掉,
-
-
游戲的邏輯
-
持續生成黑塊 -> 游戲開始的邏輯
通過定時器重新生成新的黑塊,重復下落
-
停止黑塊的生成 -> 游戲結束的邏輯
停止游戲開始的邏輯
-
按鍵控制黑塊消失 -> 游戲互動的邏輯
通過
keydown事件獲取 左 下 右 三個按鍵,根據按鍵和最新一行的位置,進行洗掉
-
代碼實作
別踩方塊兒的骨架和肉體
HTML 和 CSS 的代碼如下所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>別踩白塊兒</title>
<style>
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
background-color: #ddeedd;
}
main {
width: 210px;
height: 540px;
}
/* 設定文字的的樣式 */
#text {
width: 210px;
height: 150px;
text-align: center;
position: absolute;
top: -3%;
left: 50%;
transform: translateX(-50%);
}
/* 中間內容區的樣式 */
main,
#container,
.inst-arrow-left,
.inst-arrow-down,
.inst-arrow-right {
/* 水平垂直居中 */
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
#container {
height: 360px;
width: 210px;
overflow: hidden;
border: 4px solid #333;
}
table {
height: 540px;
width: 210x;
border: 1px solid #000;
/* 相鄰單元格公用一條線 */
border-collapse: collapse;
position: relative;
top: -96px;
left: 0;
}
td {
border: 1px solid #333;
height: 90px;
width: 70px;
}
/* 游戲說明樣式 */
#instructions {
width: 210px;
position: absolute;
top: 15%;
left: 50%;
transform: translateX(-50%);
}
#instructions>.arrow>div {
float: left;
width: 70px;
height: 90px;
position: relative;
top: 360px;
}
#instructions .arrow .arrow-img {
display: block;
width: 30px;
}
.inst-arrow-left {
transform: rotate(-90deg) translateY(-50%) translateX(50%);
}
.inst-arrow-right {
transform: rotate(90deg) translateY(50%) translateX(-50%);
}
.inst-arrow-down {
transform: rotate(180deg) translateY(50%) translateX(50%);
}
/* 黑色方塊的 CSS 樣式 */
.test1,
.test2,
.test3 {
width: 65.2px;
height: 88.4px;
display: table-cell;
position: absolute;
top: -92px;
background-color: #000;
}
.test1 {
left: 0%;
}
.test2 {
left: 33.33%;
}
.test3 {
left: 66.66%;
}
#button {
position: absolute;
width: 210px;
height: 50px;
}
#gameStard,
#gameEnd {
margin: 520px 15px;
}
</style>
</head>
<body>
<main>
<!-- 計時器和分數 -->
<div id="text">
<h2>別踩白塊兒</h2>
<p>分數 : <stan id="span">0</stan>
</p>
<!-- <p>時間 : <span>0</span></p> -->
</div>
<!-- 中間白塊區域 -->
<div id="container">
<table>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
</table>
</div>
<div id="instructions">
<div class="arrow">
<div><img class="arrow-img inst-arrow-left" src="./imgs/jiantou12-copy.png"></div>
<div><img class="arrow-img inst-arrow-down" src="./imgs/jiantou12-copy.png"></div>
<div><img class="arrow-img inst-arrow-right" src="./imgs/jiantou12-copy.png"></div>
</div>
</div>
<div id="button">
<button id="gameStard">游戲開始</button>
<button id="gameEnd">游戲結束</button>
</div>
</main>
<script src="./behavior2.js"></script>
</body>
</html>
代碼中注意的點:
- 盒子模型中由于邊框和邊距會最終回應我們盒子的內容區的大小,我們設定屬性
box-sizing: border-box;固定盒子的大小 - 表格中上面和下面在視覺區留出一行的區域做生成和判斷
效果圖如下所示

別踩方塊兒的靈魂
實作流程:
- 定義一個黑色方塊建構式,其中包含兩個方法,一個是創建新行的方法,另一個是向下移動的方法
- 定義一個游戲的建構式,此建構式通過借助建構式的方式繼承于創建黑色方塊的建構式,此物件中定義三個方法:
- 游戲開始的方法
- 游戲結束的方法
- 游戲互動的方法
- 完成點擊 游戲開始 按鈕開始游戲,和點擊 游戲結束 按鈕結束游戲的邏輯
示例代碼如下所示:
// 獲取容器節點
var container = document.getElementById('container')
// 獲取body 方便系結事件
var body = document.body
var span = document.getElementById('span')
/*
* 定義黑塊的建構式
* 屬性: flag -> 方塊是否到達底部的標志
* 方法: creBlackBlockLine(container) -> 創建一個新的黑塊的方法
* 方法: moveDownward(div) -> 方塊移動的方法
*/
function CreBlackBlocks() {
// 方塊是否到達底部的標志
this.flag = false
// 創建一個新的黑塊的方法
this.creBlackBlockLine = function (container) {
// 創建 div 節點
var div = document.createElement('div')
// 位置,表示當前方塊在第幾行
var location
container.appendChild(div)
// 隨機為 div 分配 class 屬性
var random = Math.random();
if (random <= 0.33) {
div.setAttribute('class', 'test1')
location = 0
} else if (random > 0.33 && random <= 0.66) {
div.setAttribute('class', 'test2')
location = 1
} else {
div.setAttribute('class', 'test3')
location = 2
}
return {
// 回傳一個物件,包含位置和節點
myElement: div,
myLocation: location
}
}
this.moveDownward = function (div) {
// 獲取有效屬性
var divStyles = window.getComputedStyle(div.myElement)
// 獲取行內樣式樣
var divStyle = div.myElement.style
var divStyleTop = parseInt(divStyles.top)
var t = setInterval(() => {
// 行內蓋外聯
divStyleTop++
divStyle.top = divStyleTop + "px"
// 越界則洗掉
if (divStyleTop >= 360) {
clearInterval(t)
div.myElement.remove()
}
}, 1)
}
}
/*
* 定義游戲物件, 繼承于 CreBlackBlocks 建構式
* 屬性: divElementArr -> 存盤創建的陣列
* 屬性: divPosArr -> 每一行位置的陣列
* 屬性: score -> 分數屬性
* 方法: theGameStartsNow() -> 開始的方法
* 方法: gameOver() -> 結束的方法
* 方法: keyReaction() -> 互動的邏輯
*/
function Game(container) {
// 存盤創建每一行的回傳值
var newDiv
// 存盤創建的陣列
this.divElementArr = []
// 每一行位置的陣列
this.divPosArr = []
// 借助建構式繼承
CreBlackBlocks.call(this, container)
// 分數屬性
this.score = 0
// 定義定義開始
this.theGameStartsNow = function (game) {
var t = setInterval(() => {
newDiv = this.creBlackBlockLine(container)
this.moveDownward(newDiv)
game.divElementArr.push(newDiv.myElement)
// 存盤每一個方塊第幾行的陣列
game.divPosArr.push(newDiv.myLocation)
// 沒有按到方塊結束的邏輯
if (parseInt(window.getComputedStyle(this.divElementArr[0]).top) >= 357) {
game.flag = true
}
// 結束執行的行為
if (game.flag == true) {
clearInterval(t)
for (div in game.divElementArr) {
game.divElementArr[div].remove()
}
alert('游戲結束!\n分數為:' + game.score)
game.flag = false
// 游戲資料初始化
game.score = 0
game.divElementArr = []
game.divPosArr = []
}
}, 360);
return t
}
// 定義游戲結束
this.gameOver = function (t, game) {
for (div in game.divElementArr) {
game.divElementArr[div].remove()
}
clearInterval(t)
alert('游戲結束!\n分數為:' + game.score)
// 游戲資料初始化
game.score = 0
game.divElementArr = []
game.divPosArr = []
}
// 鍵位點擊做出相應的動作
this.keyReaction = function (game, t) {
body.addEventListener('keydown', function (event) {
// 通過 switch 陳述句 根據當前的黑塊的位置進入邏輯
// Array.splice()方法 -> 作用,洗掉中的元素并回傳一個陣列,第一個引數 位置第二個引數數量
switch (event.code) {
case 'ArrowLeft':
if (game.divPosArr[0] === 0) {
game.divPosArr.splice(0, 1)
game.score += 10
span.innerHTML = game.score
game.divElementArr.splice(0, 1)[0].remove()
}
break;
case 'ArrowDown':
if (game.divPosArr[0] === 1) {
game.divPosArr.splice(0, 1)
game.score += 10
span.innerHTML = game.score
game.divElementArr.splice(0, 1)[0].remove()
}
break;
case 'ArrowRight':
if (game.divPosArr[0] === 2) {
game.divPosArr.splice(0, 1)
game.score += 10
span.innerHTML = game.score
game.divElementArr.splice(0, 1)[0].remove()
}
break;
default:
break;
}
})
}
}
var game = new Game(container)
var t
// 游戲開始
var gameStard = document.getElementById('gameStard')
gameStard.addEventListener('click', function () {
span.innerHTML = 0
t = game.theGameStartsNow(game)
game.keyReaction(game, t)
})
// 游戲結束
var gameEnd = document.getElementById('gameEnd')
gameEnd.addEventListener('click', function () {
game.gameOver(t, game)
})
遇到的問題:
-
在獲取位置的時候定義一個變數用于存盤第一行的位置,當生成新的行時,舊的值就會被替換,導致在洗掉的程序中遇到了問題
解決方案:通過這種問題想到了 佇列 ,佇列的特點是先進先出,佇列在 JavaScript 可以使用陣列實作,通過陣列存盤每一行的位置,最前面的一行就是最新的,
-
在洗掉一樣的時候,遇到了和陣列同樣的問題,就是被重新賦值,依舊通過 JavaScript 中的陣列幫我們解決這個問題,
寫在最后
這就是別踩方塊兒的全部代碼,當然還有很多可以優化的地方,等到有時候回頭寫吧,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/204181.html
標籤:其他
上一篇:躲藏
