HTML+CSS+JavaScript創(chuàng)建一個簡單的井字游戲

實現(xiàn) HTML
首先在 head 部分,我將包含我們稍后創(chuàng)建的 css 和 javascript 文件。我還添加了名為 Itim 的 Google 字體。
<link rel="stylesheet" href="style.css" rel="external nofollow" > <link rel="preconnect" rel="external nofollow" > <link rel="external nofollow" rel="stylesheet"> <script src="index.js"></script>
HTML 的主體將相當(dāng)簡單。為了包裝所有東西,我將使用一個主標(biāo)簽,并對其應(yīng)用一個類background。在main包裝器內(nèi)部,我們將有五個部分。
第一部分將只包含我們的標(biāo)題h1。
第二部分將顯示當(dāng)前輪到誰。在顯示中,我們有一個包含X或O取決于當(dāng)前用戶的跨度。我們將類應(yīng)用于此跨度以對文本進行著色。
第三部分是拿著游戲板的部分。它有一個container類,因此我們可以正確放置瓷磚。在本節(jié)中,我們有 9 個 div,它們將充當(dāng)板內(nèi)的瓷磚。
第四部分將負責(zé)公布最終比賽結(jié)果。默認情況下它是空的,我們將從 javascript 修改它的內(nèi)容。
最后一部分將保存我們的控件,其中包含一個重新開始按鈕。
<main class="background">
<section class="title">
<h1>井字游戲</h1>
</section>
<section class="display">
玩家 <span class="display-player playerX">X</span> 的回合
</section>
<section class="container">
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
</section>
<section class="display announcer hide"></section>
<section class="controls">
<button id="reset">重新開始</button>
</section>
</main>
添加 CSS
我不會詳細介紹 CSS 的每一行,但你可以查看源碼中的完整代碼。
首先,我將創(chuàng)建style.css文件并刪除任何瀏覽器定義的邊距和填充,并為整個文檔設(shè)置我在 HTML 中包含的 Google 字體。
* {
padding: 0;
margin: 0;
font-family: 'Itim', cursive;
}
我們必須添加的下一件重要事情是我們的板的樣式。我們將使用 CSS 網(wǎng)格來創(chuàng)建板。我們可以通過為列和行提供 3 倍 33% 的空間將容器一分為二。我們將通過設(shè)置最大寬度和將容器居中margin: 0 auto;。
.container {
margin: 0 auto;
display: grid;
grid-template-columns: 33% 33% 33%;
grid-template-rows: 33% 33% 33%;
max-width: 300px;
}
接下來,我們將添加板內(nèi)瓷磚的樣式。我們將應(yīng)用一個小的白色邊框,并將最小寬度和高度設(shè)置為 100 像素。我們將利用Flexbox的和設(shè)置的中心內(nèi)容justify-content 和 align-items到center。我們會給它一個大字體大小并應(yīng)用,cursor: pointer這樣用戶就會知道這個字段是可點擊的。
.tile {
border: 1px solid white;
min-width: 100px;
min-height: 100px;
display: flex;
justify-content: center;
align-items: center;
font-size: 50px;
cursor: pointer;
}
我使用了兩種不同的顏色來更好地區(qū)分這兩個玩家。為此,我創(chuàng)建兩個實用程序類。玩家 X 的顏色為綠色,而玩家 O 的顏色為藍色。
.playerX {
color: #09C372;
}
.playerO {
color: #498AFB;
}
實現(xiàn) Javascript 部分
由于我們將 javascript 文件包含在<head>. 這是必需的,因為我們的腳本將在瀏覽器解析 HTML 正文之前加載。如果你不想將所有內(nèi)容都包含在此函數(shù)中,請隨意添加defer到腳本標(biāo)記中或?qū)⒛_本標(biāo)記移動到body.
window.addEventListener('DOMContentLoaded', () => {
});
首先,我們將保存對 DOM 節(jié)點的引用。我們將使用document.querySelectorAll(). 我們想要一個數(shù)組,但此函數(shù)返回一個 NodeList,因此我們必須使用Array.from(). 我們還將獲取對播放器顯示、重置按鈕和播音員的引用。
const tiles = Array.from(document.querySelectorAll('.tile'));
const playerDisplay = document.querySelector('.display-player');
const resetButton = document.querySelector('#reset');
const announcer = document.querySelector('.announcer');
接下來,我們將添加控制游戲所需的全局變量。我們將用一個包含九個空字符串的數(shù)組來初始化一個板。這將保存板上每個圖塊的 X abd O 值。我們將有一個currentPlayer持有當(dāng)前回合活躍的玩家的標(biāo)志。該isGameActive變量將一直為真,直到有人獲勝或游戲以平局結(jié)束。在這些情況下,我們會將其設(shè)置為 false,以便剩余的圖塊在重置之前處于非活動狀態(tài)。我們有三個常數(shù)代表游戲結(jié)束狀態(tài)。我們使用這些常量來避免拼寫錯誤。
let board = ['', '', '', '', '', '', '', '', '']; let currentPlayer = 'X'; let isGameActive = true; const PLAYERX_WON = 'PLAYERX_WON'; const PLAYERO_WON = 'PLAYERO_WON'; const TIE = 'TIE';
在下一步中,我們將在棋盤上存儲所有獲勝的位置。在每個子數(shù)組中,我們將存儲可以贏得比賽的三個位置的索引。所以這[0, 1, 2]將代表第一條水平線被玩家占據(jù)的情況。我們將使用這個數(shù)組來決定我們是否有贏家。
/* Indexes within the board [0] [1] [2] [3] [4] [5] [6] [7] [8] */ const winningConditions = [ [0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6] ];
現(xiàn)在我們將編寫一些實用函數(shù)。在isValidAction函數(shù)中,我們將決定用戶是否想要執(zhí)行有效的操作。如果 tile 的內(nèi)部文本是XorO我們返回 false 作為操作無效,否則 tile 為空所以操作有效。
const isValidAction = (tile) => {
if (tile.innerText === 'X' || tile.innerText === 'O'){
return false;
}
return true;
};
下一個效用函數(shù)將非常簡單。在這個函數(shù)中,我們將接收一個索引作為參數(shù),并將棋盤數(shù)組中的相應(yīng)元素設(shè)置為我們當(dāng)前玩家的符號。
const updateBoard = (index) => {
board[index] = currentPlayer;
}
我們將編寫一個小函數(shù)來處理玩家的變化。在這個函數(shù)中,我們將首先從playerDisplay. 字符串模板文字player${currentPlayer}將成為playerX或playerO取決于當(dāng)前玩家。接下來,我們將使用三元表達式來更改當(dāng)前玩家的值。如果是X,它將是O否則它將是X。現(xiàn)在,我們改變了我們用戶的價值,我們需要更新innerText的playerDisplay,并應(yīng)用新的播放器類的。
const changePlayer = () => {
playerDisplay.classList.remove(`player${currentPlayer}`);
currentPlayer = currentPlayer === 'X' ? 'O' : 'X';
playerDisplay.innerText = currentPlayer;
playerDisplay.classList.add(`player${currentPlayer}`);
}
現(xiàn)在我們將編寫宣布最終游戲結(jié)果的 announer 函數(shù)。它將接收結(jié)束游戲類型并innerText根據(jù)結(jié)果更新播音員 DOM 節(jié)點的 。在最后一行中,我們必須刪除隱藏類,因為播音員默認是隱藏的,直到游戲結(jié)束。
const announce = (type) => {
switch(type){
case PLAYERO_WON:
announcer.innerHTML = 'Player <span class="playerO">O</span> Won';
break;
case PLAYERX_WON:
announcer.innerHTML = 'Player <span class="playerX">X</span> Won';
break;
case TIE:
announcer.innerText = 'Tie';
}
announcer.classList.remove('hide');
};
接下來我們將編寫這個項目中最有趣的部分之一——結(jié)果評估。首先,我們將創(chuàng)建一個 roundWon 變量并將其初始化為 false。然后我們將遍歷winConditions數(shù)組并檢查棋盤上的每個獲勝條件。例如,在第二次迭代中,我們將檢查這些值:board3、board4、board5。
我們還將進行一些優(yōu)化,如果任何字段為空,我們將調(diào)用continue并跳到下一次迭代,因為如果獲勝條件中有空圖塊,您將無法獲勝。如果所有字段都相等,那么我們就有一個贏家,因此我們將 roundWon 設(shè)置為 true 并中斷 for 循環(huán),因為任何進一步的迭代都會浪費計算。
在循環(huán)之后,我們將檢查roundWon變量的值,如果為真,我們將宣布獲勝者并將游戲設(shè)置為非活動狀態(tài)。如果我們沒有獲勝者,我們將檢查棋盤上是否有空牌,如果我們沒有獲勝者并且沒有空牌,我們將宣布平局。
function handleResultValidation() {
let roundWon = false;
for (let i = 0; i <= 7; i++) {
const winCondition = winningConditions[i];
const a = board[winCondition[0]];
const b = board[winCondition[1]];
const c = board[winCondition[2]];
if (a === "" || b === "" || c === "") {
continue;
}
if (a === b && b === c) {
roundWon = true;
break;
}
}
if (roundWon) {
announce(currentPlayer === "X" ? PLAYERX_WON : PLAYERO_WON);
isGameActive = false;
return;
}
if (!board.includes("")) announce(TIE);
}
接下來我們將處理用戶的操作。此函數(shù)將接收一個 tile 和一個索引作為參數(shù)。當(dāng)用戶單擊一個圖塊時,將調(diào)用此函數(shù)。首先我們需要檢查它是否是一個有效的動作,我們還將檢查游戲當(dāng)前是否處于活動狀態(tài)。如果兩者都為真,我們innerText用當(dāng)前玩家的符號更新瓷磚的 ,添加相應(yīng)的類并更新板陣列。現(xiàn)在一切都更新了,我們必須檢查游戲是否已經(jīng)結(jié)束,所以我們調(diào)用handleResultValidation(). 最后,我們必須調(diào)用該changePlayer方法將輪次傳遞給另一個玩家。
const userAction = (tile, index) => {
if (isValidAction(tile) && isGameActive) {
tile.innerText = currentPlayer;
tile.classList.add(`player${currentPlayer}`);
updateBoard(index);
handleResultValidation();
changePlayer();
}
};
為了讓游戲正常運行,我們必須向磁貼添加事件偵聽器。我們可以通過循環(huán)遍歷圖塊數(shù)組并為每個圖塊添加一個事件偵聽器來做到這一點。(為了獲得更好的性能,我們只能向容器添加一個事件偵聽器并使用事件冒泡來捕獲父級上的磁貼點擊,但我認為對于初學(xué)者來說這更容易理解。)
tiles.forEach( (tile, index) => {
tile.addEventListener('click', () => userAction(tile, index));
});
我們只錯過了一項功能:重置游戲。為此,我們將編寫一個resetBoard函數(shù)。在此函數(shù)中,我們將棋盤設(shè)置X為由九個空字符串組成,將游戲設(shè)置為活動狀態(tài),移除播音員并將玩家更改回(根據(jù)定義X始終開始)。
我們必須做的最后一件事是遍歷圖塊并將innerText 設(shè)置回空字符串,并從圖塊中刪除任何特定于玩家的類。
const resetBoard = () => {
board = ['', '', '', '', '', '', '', '', ''];
isGameActive = true;
announcer.classList.add('hide');
if (currentPlayer === 'O') {
changePlayer();
}
tiles.forEach(tile => {
tile.innerText = '';
tile.classList.remove('playerX');
tile.classList.remove('playerO');
});
}
現(xiàn)在我們只需要將此函數(shù)注冊為重置按鈕的點擊事件處理程序。
resetButton.addEventListener('click', resetBoard);
就是這樣,我們有一個功能齊全的井字游戲,你可以和你的朋友一起玩,玩得開心。
以上就是HTML+CSS+JavaScript創(chuàng)建一個簡單的井字游戲的詳細內(nèi)容,更多關(guān)于HTML+CSS+JavaScript的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
javascript中Array()數(shù)組函數(shù)詳解
在JavaScript中數(shù)組也是比較常用的對象之一,數(shù)組是值的有序集合,本篇文章給大家分享Javascript中Array()數(shù)組函數(shù)詳解,需要的朋友可以參考下2015-08-08
基于JS代碼實現(xiàn)簡單易用的倒計時 x 天 x 時 x 分 x 秒效果
這篇文章主要介紹了基于JS代碼實現(xiàn)簡單易用的倒計時 x 天 x 時 x 分 x 秒效果,需要的朋友可以參考下2017-07-07
IE8的JavaScript點擊事件(onclick)不兼容的解決方法
這篇文章主要介紹了IE8的JavaScript點擊事件(onclick)不兼容的解決方法,大家參考使用吧2013-11-11
javascript異步編程代碼書寫規(guī)范Promise學(xué)習(xí)筆記
這篇文章主要介紹了javascript異步編程代碼書寫規(guī)范Promise學(xué)習(xí)筆記,需要的朋友可以參考下2015-02-02
JS使用canvas中的measureText方法測量字體寬度示例
這篇文章主要介紹了JS使用canvas中的measureText方法測量字體寬度,結(jié)合實例形式分析了canvas的measureText方法相關(guān)使用技巧,需要的朋友可以參考下2019-02-02
JS實現(xiàn)瀏覽器狀態(tài)欄文字從右向左彈出效果代碼
這篇文章主要介紹了JS實現(xiàn)瀏覽器狀態(tài)欄文字從右向左彈出效果,涉及JavaScript結(jié)合時間函數(shù)遍歷字符串及動態(tài)改變狀態(tài)欄顯示效果的相關(guān)技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-10-10

