溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

怎么用JavaScript寫一個(gè)卡片小游戲

發(fā)布時(shí)間:2021-08-31 18:02:43 來源:億速云 閱讀:156 作者:chen 欄目:web開發(fā)

這篇文章主要介紹“怎么用JavaScript寫一個(gè)卡片小游戲”,在日常操作中,相信很多人在怎么用JavaScript寫一個(gè)卡片小游戲問題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”怎么用JavaScript寫一個(gè)卡片小游戲”的疑惑有所幫助!接下來,請(qǐng)跟著小編一起來學(xué)習(xí)吧!

項(xiàng)目結(jié)構(gòu)
先在終端中創(chuàng)建項(xiàng)目文件:

mkdir memory-game
cd memory-game
touch index.html styles.css
scripts.js mkdir img

HTML

初始化頁面模版并鏈接 css 文件 js 文件.

<!-- index.html --> <!DOCTYPE html> <html> <head>   <meta charset="UTF-8">   <title>Memory Game</title>   <link rel="stylesheet" href="./styles.css"> </head> <body>   <script src="./scripts.js"></script> </body> </html>

這個(gè)游戲有 12 張卡片。 每張卡片中都包含一個(gè)名為 .memory-card 的容器 div,它包含兩個(gè)img元素。 一個(gè)代表卡片的正面 front-face ,另一個(gè)個(gè)代表背面 back-face。

怎么用JavaScript寫一個(gè)卡片小游戲

<div>   <img src="img/react.svg" alt="React">   <img src="img/js-badge.svg" alt="Memory Card"> </div>

這組卡片將被包裝在一個(gè) section 容器元素中。 最終代碼如下:

<!-- index.html --> <section>   <div>     <img src="img/react.svg" alt="React">     <img src="img/js-badge.svg" alt="Memory Card">   </div>   <div>     <img src="img/react.svg" alt="React">     <img src="img/js-badge.svg" alt="Memory Card">   </div>   <div>     <img src="img/angular.svg" alt="Angular">     <img src="img/js-badge.svg" alt="Memory Card">   </div>   <div>     <img src="img/angular.svg" alt="Angular">     <img src="img/js-badge.svg" alt="Memory Card">   </div>   <div>     <img src="img/ember.svg" alt="Ember">     <img src="img/js-badge.svg" alt="Memory Card">   </div>   <div>     <img src="img/ember.svg" alt="Ember">     <img src="img/js-badge.svg" alt="Memory Card">   </div>   <div>     <img src="img/vue.svg" alt="Vue">     <img src="img/js-badge.svg" alt="Memory Card">   </div>   <div>     <img src="img/vue.svg" alt="Vue">     <img src="img/js-badge.svg" alt="Memory Card">   </div>   <div>     <img src="img/backbone.svg" alt="Backbone">     <img src="img/js-badge.svg" alt="Memory Card">   </div>   <div>     <img src="img/backbone.svg" alt="Backbone">     <img src="img/js-badge.svg" alt="Memory Card">   </div>   <div>     <img src="img/aurelia.svg" alt="Aurelia">     <img src="img/js-badge.svg" alt="Memory Card">   </div>   <div>     <img src="img/aurelia.svg" alt="Aurelia">     <img src="img/js-badge.svg" alt="Memory Card">   </div> </section>

CSS

我們將使用一個(gè)簡(jiǎn)單但非常有用的配置,把它應(yīng)用于所有項(xiàng)目:

/* styles.css */ * {   padding: 0;   margin: 0;   box-sizing: border-box; }

box-sizing: border-box 屬性能使元素充滿整個(gè)邊框,所以我們就可以不用做一些數(shù)學(xué)計(jì)算了。

把 display:flex 設(shè)置給 body ,并且把 margin:auto應(yīng)用到到 .memory-game 容器,這樣可以使它將垂直水平居中。

.memory-game 是一個(gè)彈性容器,在默認(rèn)情況下,里面的元素會(huì)縮小寬度來適應(yīng)這個(gè)容器。通過把 flex-wrap 的值設(shè)置為 wrap,會(huì)根據(jù)彈性元素的大小進(jìn)行自適應(yīng)。

/* styles.css */ body {   height: 100vh;   display: flex;   background: #060AB2; } .memory-game {   width: 640px;   height: 640px;   margin: auto;   display: flex;   flex-wrap: wrap; }

每個(gè)卡片的 width 和 height 都是用 CSS 的 calc()函數(shù)進(jìn)行計(jì)算的。 下面我們需要制作一個(gè)三行四列的界面,并且把 width 設(shè)置為 25%, height 設(shè)置為 33.333% ,還要再減去 10px 留足邊距.

為了定位 .memory-card 子元素,還要添加屬性 position: relative ,這樣我們就可以相對(duì)它進(jìn)行子元素的絕對(duì)定位。

把 front-face and back-face 的position屬性都設(shè)置為 absolute ,這樣就可以從原始位置移除元素,并使它們堆疊在一起。

這時(shí)頁面模版看上去應(yīng)該是這樣:

怎么用JavaScript寫一個(gè)卡片小游戲

我們還需要添加一個(gè)點(diǎn)擊效果。 每次元素被點(diǎn)擊時(shí)都會(huì)觸發(fā) :active 偽類,它引發(fā)一個(gè) 0.2秒的過渡:

怎么用JavaScript寫一個(gè)卡片小游戲

翻轉(zhuǎn)卡片

要在單擊時(shí)翻轉(zhuǎn)卡片,需要把一個(gè) flip 類添加到元素。 為此,讓我們用 document.querySelectorAll 選擇所有 memory-card 元素,然后使用 forEach 遍歷它們并附加一個(gè)事件監(jiān)聽器。 每當(dāng)卡片被點(diǎn)擊時(shí),都會(huì)觸發(fā) flipCard 函數(shù),其中 this 代表被單擊的卡片。 該函數(shù)訪問元素的 classList 并切換到 flip 類:

// scripts.js const cards = document.querySelectorAll('.memory-card'); function flipCard() {   this.classList.toggle('flip'); } cards.forEach(card => card.addEventListener('click', flipCard));

CSS 中的 flip 類會(huì)把卡片旋轉(zhuǎn) 180deg:

.memory-card.flip {   transform: rotateY(180deg); }

為了產(chǎn)生3D翻轉(zhuǎn)效果,還需要將 perspective 屬性添加到 .memory-game。 這個(gè)屬性用來設(shè)置對(duì)象與用戶在 z 軸上的距離。 值越小,透視效果越強(qiáng)。 為了能達(dá)得最佳的效果,把它設(shè)置為 1000px:

.memory-game {   width: 640px;   height: 640px;   margin: auto;   display: flex;   flex-wrap: wrap; + perspective: 1000px; }

接下來對(duì) .memory-card 元素添加 transform-style:preserve-3d屬性,這樣就把卡片置于在父節(jié)點(diǎn)中創(chuàng)建的3D空間中,而不是將其平鋪在 z = 0 的平面上(transform-style)。

.memory-card {   width: calc(25% - 10px);   height: calc(33.333% - 10px);   margin: 5px;   position: relative;   box-shadow: 1px 1px 1px rgba(0,0,0,.3);   transform: scale(1); + transform-style: preserve-3d; }

再把 transition 屬性的值設(shè)置為 transform 就可以生成動(dòng)態(tài)效果了

.memory-card {   width: calc(25% - 10px);   height: calc(33.333% - 10px);   margin: 5px;   position: relative;   box-shadow: 1px 1px 1px rgba(0,0,0,.3);   transform: scale(1);   transform-style: preserve-3d; + transition: transform .5s; }

現(xiàn)在我們得到了帶有 3D 翻轉(zhuǎn)效果的卡片, 不過為什么卡片的另一面沒有出現(xiàn)? 由于絕對(duì)定位的原因,現(xiàn)在 .front-face 和 .back-face 都堆疊在了一起。 每個(gè)元素的 back face 都是它 front face 的鏡像。 屬性 backface-visibility 默認(rèn)為 visible,因此當(dāng)我們翻轉(zhuǎn)卡片時(shí),得到的是背面的 JS 徽章。

![( http://upload-images.jianshu.io/upload_images/13133049-4521ac8b957bb1be.gif?imageMogr2/auto-orient/strip )

為了顯示它背面的圖像,讓我們?cè)?.front-face 和 .back-face 中添加 backface-visibility:hidden

.front-face, .back-face {   width: 100%;   height: 100%;   padding: 20px;   position: absolute;   border-radius: 5px;   background: #1C7CCC; + backface-visibility: hidden; }

如果我們刷新頁面并翻轉(zhuǎn)一張卡片,它就消失了!

怎么用JavaScript寫一個(gè)卡片小游戲

由于我們將兩個(gè)圖像都藏在了背面,所以另一面沒有任何東西。 所以接下來需要再把 .front-face 翻轉(zhuǎn)180度:

.front-face {   transform: rotateY(180deg); }

效果出來了!

怎么用JavaScript寫一個(gè)卡片小游戲

匹配卡片

完成翻轉(zhuǎn)卡片的功能之后,接下來處理匹配的邏輯。

當(dāng)點(diǎn)擊第一張卡片時(shí),需要等待另一張被翻轉(zhuǎn)。 變量 hasFlippedCard 和 flippedCard 用來管理翻轉(zhuǎn)狀態(tài)。 如果沒有卡片翻轉(zhuǎn),hasFlippedCard 的值為 true,flippedCard 被設(shè)置為點(diǎn)擊的卡片。 讓我們切換到 toggle 方法:

  const cards = document.querySelectorAll('.memory-card'); + let hasFlippedCard = false; + let firstCard, secondCard;   function flipCard() { -   this.classList.toggle('flip'); +   this.classList.add('flip'); +   if (!hasFlippedCard) { +     hasFlippedCard = true; +     firstCard = this; +   }   } cards.forEach(card => card.addEventListener('click', flipCard));

現(xiàn)在,當(dāng)用戶點(diǎn)擊第二張牌時(shí),代碼會(huì)進(jìn)入 else 塊,我們將檢查它們是否匹配。為了做到這一點(diǎn),需要能夠識(shí)別每一張卡片。

每當(dāng)我們想要向HTML元素添加額外信息時(shí),就可以使用數(shù)據(jù)屬性。 通過使用以下語法: data-,這里的 可以是任何單詞,它將被插入到元素的 dataset 屬性中。 所以接下來為每張卡片添加一個(gè) data-framework :

<section> + <div data-framework="react">     <img src="img/react.svg" alt="React">     <img src="img/js-badge.svg" alt="Memory Card">   </div> + <div data-framework="react">     <img src="img/react.svg" alt="React">     <img src="img/js-badge.svg" alt="Memory Card">   </div> + <div data-framework="angular">     <img src="img/angular.svg" alt="Angular">     <img src="img/js-badge.svg" alt="Memory Card">   </div> + <div data-framework="angular">     <img src="img/angular.svg" alt="Angular">     <img src="img/js-badge.svg" alt="Memory Card">   </div> + <div data-framework="ember">     <img src="img/ember.svg" alt="Ember">     <img src="img/js-badge.svg" alt="Memory Card">   </div> + <div data-framework="ember">     <img src="img/ember.svg" alt="Ember">     <img src="img/js-badge.svg" alt="Memory Card">   </div> + <div data-framework="vue">     <img src="img/vue.svg" alt="Vue">     <img src="img/js-badge.svg" alt="Memory Card">   </div> + <div data-framework="vue">     <img src="img/vue.svg" alt="Vue">     <img src="img/js-badge.svg" alt="Memory Card">   </div> + <div data-framework="backbone">     <img src="img/backbone.svg" alt="Backbone">     <img src="img/js-badge.svg" alt="Memory Card">   </div> + <div data-framework="backbone">     <img src="img/backbone.svg" alt="Backbone">     <img src="img/js-badge.svg" alt="Memory Card">   </div> + <div data-framework="aurelia">     <img src="img/aurelia.svg" alt="Aurelia">     <img src="img/js-badge.svg" alt="Memory Card">   </div> + <div data-framework="aurelia">     <img src="img/aurelia.svg" alt="Aurelia">     <img src="img/js-badge.svg" alt="Memory Card">   </div> </section>

這下就可以通過訪問兩個(gè)卡片的數(shù)據(jù)集來檢查匹配了。 下面將匹配邏輯提取到它自己的方法 checkForMatch(),并將 hasFlippedCard 設(shè)置為 false。 如果匹配的話,則調(diào)用 disableCards() 并分離兩個(gè)卡上的事件偵聽器,以防止再次翻轉(zhuǎn)。 否則 unflipCards() 會(huì)將兩張卡都恢復(fù)成超過 1500 毫秒的超時(shí),從而刪除 .flip 類:

把代碼組合起來:

const cards = document.querySelectorAll('.memory-card');   let hasFlippedCard = false;   let firstCard, secondCard;   function flipCard() {     this.classList.add('flip');     if (!hasFlippedCard) {       hasFlippedCard = true;       firstCard = this; +     return; +   } + +   secondCard = this; +   hasFlippedCard = false; + +   checkForMatch(); + } + + function checkForMatch() { +   if (firstCard.dataset.framework === secondCard.dataset.framework) { +     disableCards(); +     return; +   } + +   unflipCards(); + } + + function disableCards() { +   firstCard.removeEventListener('click', flipCard); +   secondCard.removeEventListener('click', flipCard); + } + + function unflipCards() { +   setTimeout(() => { +     firstCard.classList.remove('flip'); +     secondCard.classList.remove('flip'); +   }, 1500); + }   cards.forEach(card => card.addEventListener('click', flipCard));

更優(yōu)雅的進(jìn)行條件匹配的方法是用三元運(yùn)算符,它由三部分組成: 第一部分是要判斷的條件, 如果條件符合就執(zhí)行第二部分的代碼,否則執(zhí)行第三部分:

- if (firstCard.dataset.name === secondCard.dataset.name) { -   disableCards(); -   return; - } - - unflipCards(); + let isMatch = firstCard.dataset.name === secondCard.dataset.name; + isMatch ? disableCards() : unflipCards();

鎖定

現(xiàn)在已經(jīng)完成了匹配邏輯,接著為了避免同時(shí)轉(zhuǎn)動(dòng)兩組卡片,還需要鎖定它們,否則翻轉(zhuǎn)將會(huì)被失敗。

怎么用JavaScript寫一個(gè)卡片小游戲

先聲明一個(gè) lockBoard 變量。 當(dāng)玩家點(diǎn)擊第二張牌時(shí),lockBoard將設(shè)置為true,條件 if (lockBoard) return; 在卡被隱藏或匹配之前會(huì)阻止其他卡片翻轉(zhuǎn):

const cards = document.querySelectorAll('.memory-card');   let hasFlippedCard = false; + let lockBoard = false;   let firstCard, secondCard;   function flipCard() { +   if (lockBoard) return;     this.classList.add('flip');     if (!hasFlippedCard) {       hasFlippedCard = true;       firstCard = this;       return;     }     secondCard = this;     hasFlippedCard = false;     checkForMatch();   }   function checkForMatch() {     let isMatch = firstCard.dataset.name === secondCard.dataset.name;     isMatch ? disableCards() : unflipCards();   }   function disableCards() {     firstCard.removeEventListener('click', flipCard);     secondCard.removeEventListener('click', flipCard);   }   function unflipCards() { +     lockBoard = true;     setTimeout(() => {       firstCard.classList.remove('flip');       secondCard.classList.remove('flip'); +     lockBoard = false;     }, 1500);   }   cards.forEach(card => card.addEventListener('click', flipCard));

點(diǎn)擊同一個(gè)卡片

仍然是玩家可以在同一張卡上點(diǎn)擊兩次的情況。 如果匹配條件判斷為 true,從該卡上刪除事件偵聽器。

怎么用JavaScript寫一個(gè)卡片小游戲

為了防止這種情況,需要檢查當(dāng)前點(diǎn)擊的卡片是否等于firstCard,如果是肯定的則返回。

if (this === firstCard) return;

變量 firstCard 和 secondCard 需要在每一輪之后被重置,所以讓我們將它提取到一個(gè)新方法 resetBoard()中, 再其中寫上 hasFlippedCard = false; 和 lockBoard = false 。 es6 的解構(gòu)賦值功能 [var1, var2] = ['value1', 'value2'] 允許我們把代碼寫得超短:

function resetBoard() {   [hasFlippedCard, lockBoard] = [false, false];   [firstCard, secondCard] = [null, null]; }

接著調(diào)用新方法 disableCards() 和 unflipCards():

const cards = document.querySelectorAll('.memory-card');   let hasFlippedCard = false;   let lockBoard = false;   let firstCard, secondCard;   function flipCard() {     if (lockBoard) return; +   if (this === firstCard) return;     this.classList.add('flip');     if (!hasFlippedCard) {       hasFlippedCard = true;       firstCard = this;       return;     }     secondCard = this; -   hasFlippedCard = false;     checkForMatch();   }   function checkForMatch() {     let isMatch = firstCard.dataset.name === secondCard.dataset.name;     isMatch ? disableCards() : unflipCards();   }   function disableCards() {     firstCard.removeEventListener('click', flipCard);     secondCard.removeEventListener('click', flipCard); +   resetBoard();   }   function unflipCards() {     lockBoard = true;     setTimeout(() => {       firstCard.classList.remove('flip');       secondCard.classList.remove('flip'); -     lockBoard = false; +     resetBoard();     }, 1500);   } + function resetBoard() { +   [hasFlippedCard, lockBoard] = [false, false]; +   [firstCard, secondCard] = [null, null]; + }   cards.forEach(card => card.addEventListener('click', flipCard));

點(diǎn)擊同一個(gè)卡片

仍然是玩家可以在同一張卡上點(diǎn)擊兩次的情況。 如果匹配條件判斷為 true,從該卡上刪除事件偵聽器。

怎么用JavaScript寫一個(gè)卡片小游戲

為了防止這種情況,需要檢查當(dāng)前點(diǎn)擊的卡片是否等于firstCard,如果是肯定的則返回。

if (this === firstCard) return;

變量 firstCard 和 secondCard 需要在每一輪之后被重置,所以讓我們將它提取到一個(gè)新方法 resetBoard()中, 再其中寫上 hasFlippedCard = false; 和 lockBoard = false 。 es6 的解構(gòu)賦值功能 [var1, var2] = ['value1', 'value2'] 允許我們把代碼寫得超短:

function resetBoard() {   [hasFlippedCard, lockBoard] = [false, false];   [firstCard, secondCard] = [null, null]; }

接著調(diào)用新方法 disableCards() 和 unflipCards():

const cards = document.querySelectorAll('.memory-card');   let hasFlippedCard = false;   let lockBoard = false;   let firstCard, secondCard;   function flipCard() {     if (lockBoard) return; +   if (this === firstCard) return;     this.classList.add('flip');     if (!hasFlippedCard) {       hasFlippedCard = true;       firstCard = this;       return;     }     secondCard = this; -   hasFlippedCard = false;     checkForMatch();   }   function checkForMatch() {     let isMatch = firstCard.dataset.name === secondCard.dataset.name;     isMatch ? disableCards() : unflipCards();   }   function disableCards() {     firstCard.removeEventListener('click', flipCard);     secondCard.removeEventListener('click', flipCard); +   resetBoard();   }   function unflipCards() {     lockBoard = true;     setTimeout(() => {       firstCard.classList.remove('flip');       secondCard.classList.remove('flip'); -     lockBoard = false; +     resetBoard();     }, 1500);   } + function resetBoard() { +   [hasFlippedCard, lockBoard] = [false, false]; +   [firstCard, secondCard] = [null, null]; + }   cards.forEach(card => card.addEventListener('click', flipCard));

洗牌

我們的游戲看起來相當(dāng)不錯(cuò),但是如果不能洗牌就沒有樂趣,所以現(xiàn)在處理這個(gè)功能。

當(dāng) display: flex 在容器上被聲明時(shí),flex-items 會(huì)按照組和源的順序進(jìn)行排序。 每個(gè)組由order屬性定義,該屬性包含正整數(shù)或負(fù)整數(shù)。 默認(rèn)情況下,每個(gè) flex-item 都將其 order 屬性設(shè)置為 0,這意味著它們都屬于同一個(gè)組,并將按源的順序排列。 如果有多個(gè)組,則首先按組升序順序排列。

游戲中有12張牌,因此我們將迭代它們,生成 0 到 12 之間的隨機(jī)數(shù)并將其分配給 flex-item order 屬性:

function shuffle() {   cards.forEach(card => {     let ramdomPos = Math.floor(Math.random() * 12);     card.style.order = ramdomPos;   }); }

為了調(diào)用 shuffle 函數(shù),讓它成為一個(gè)立即調(diào)用函數(shù)表達(dá)式(IIFE),這意味著它將在聲明后立即執(zhí)行。 腳本應(yīng)如下所示:

const cards = document.querySelectorAll('.memory-card');   let hasFlippedCard = false;   let lockBoard = false;   let firstCard, secondCard;   function flipCard() {     if (lockBoard) return;     if (this === firstCard) return;     this.classList.add('flip');     if (!hasFlippedCard) {       hasFlippedCard = true;       firstCard = this;       return;     }     secondCard = this;     lockBoard = true;     checkForMatch();   }   function checkForMatch() {     let isMatch = firstCard.dataset.name === secondCard.dataset.name;     isMatch ? disableCards() : unflipCards();   }   function disableCards() {     firstCard.removeEventListener('click', flipCard);     secondCard.removeEventListener('click', flipCard);     resetBoard();   }   function unflipCards() {     setTimeout(() => {       firstCard.classList.remove('flip');       secondCard.classList.remove('flip');       resetBoard();     }, 1500);   }   function resetBoard() {     [hasFlippedCard, lockBoard] = [false, false];     [firstCard, secondCard] = [null, null];   } + (function shuffle() { +   cards.forEach(card => { +     let ramdomPos = Math.floor(Math.random() * 12); +     card.style.order = ramdomPos; +   }); + })();   cards.forEach(card => card.addEventListener('click', flipCard));

到此,關(guān)于“怎么用JavaScript寫一個(gè)卡片小游戲”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI