基于JavaScript編寫一個翻卡游戲
前言
首先將這個游戲需求拆分成三個部分:
- 翻卡動畫
- 生成隨機分布數(shù)組
- 點擊事件
翻卡動畫
假如我們的盒子模型不是個二維的平面,而是有個三維的體積,讓它可以有正反兩面,那我們在做的時候是不是只要將它真實的翻個面就可以了。讓我們來想想將它變成三維的方法。 之后發(fā)現(xiàn)了這個屬性:
transform: translateZ(1px);
使用了它,就可以把盒子內部的元素與盒子的底部撐出個高度。
<!-- html -->
<div class="card">
<div class="top">我是正面哦~</div>
</div>只用給叫做“top”的子盒子一個“距離父親的距離”,再將叫做“card”的父盒子預先翻轉180度rotateY(180deg),等到點擊的時候給它翻回來transform: rotateY(0)就可以了。
.card{
...
height: 100%;
width: 100%;
position: relative;
transform-style: preserve-3d;
transform: rotateY(180deg);
transition: all 600ms;
background: pink;
&.select {
transform: rotateY(0);
}
.top{
...
height: 100%;
width: 100%;
position: absolute;
top: 0;
left: 0;
box-sizing: border-box;
background: white;
border: 2px solid #b6a6dc;
transform: translateZ(1px);
}
}生成隨機分布數(shù)組
我們先來說下在理想環(huán)境中,每個元素都能勻均出現(xiàn)(次數(shù)相等)的情況。再來說下不能均勻出現(xiàn)的情況下,怎樣最大限度的均勻。
均勻元素下的隨機算法
此算法腦內模型由西塔(θ)先生友情提供
假設我們一共需要20個元素,有5個不同類型的格子,正好每個格子出現(xiàn)4次。我們就有了一個待分配元素的集合W:
const total = 20
const icons = ['a', 'b', 'c', 'd', 'e']
// => 得到集合W
const W = ['a', 'a', 'a', 'a',
'b', 'b', 'b', 'b',
'c', 'c', 'c', 'c',
'd', 'd', 'd', 'd',
'e', 'e', 'e', 'e']
混淆集合
有個指針p從下標0開始,在長度為20的數(shù)組格子里面負責填圖案,填圖案的規(guī)律是從集合w中隨機取一個元素,取完后刪除該元素,p移動到下一個格子里,迭代至完成。
function createRandomList(W: string[], total: number) {
const list: any[] = []
function it(time: number): any {
if (time === 0) return list
// 隨機每次集合元素下標
const randomNum = Math.floor(Math.random() * (W.length))
list.push(W[randomNum]) // 新數(shù)組中加入隨機到的元素
W.splice(randomNum, 1) // 刪除集合中的元素
return it(--time)
}
return it(total)
}我們再讓這個方法靈活一點,使它的返回結果能夠隨便指定格式:
// fn非必傳項,默認返回原數(shù)據(jù)
function createRandomList(W: string[], total: number, fn: (<T>(icon: string, index?: number) => T) = icon => icon) {
const list: any[] = []
// 迭代器
function it(time: number): any {
if (time === 0) return list
// 隨機每次集合元素下標
const randomNum = Math.floor(Math.random() * (W.length))
list.push(fn(W[randomNum], total-time)) // 將元素和下標傳入fn中,將fn的計算結果加入到新數(shù)組中
W.splice(randomNum, 1) // 刪除集合中的元素
return it(--time)
}
return it(total)
}不均勻元素下的隨機算法
const W = []
不均勻元素,其實就是集合W里的元素分布規(guī)則改變了,混淆算法仍然不受影響。之后,讓我們來思考下怎么定義一個“不均勻中的最大程度均勻”的集合。 將集合W分為兩個部分: 最大可均勻分布部分 + 隨機部分
最大可均勻分布的部分,它代表著icons中的每個元素都能出現(xiàn)相同的最多偶數(shù)次。可以這樣得到它:
- icons個數(shù)x2,得到完整一次配對需要多少格子
- 總格子數(shù) / 一次完整格子數(shù),得到可以完整配對的最大次數(shù)n
- 循環(huán)n次,每次循環(huán)往W里添加icons x 2
// 得到最大重復次數(shù)
const times = Math.floor(total / (icons.length * 2))
for (let index = 0; index < times; index++)
W.push(...icons, ...icons)
剩下的是需要隨機分布的部分,它代表著,某幾個元素可以在這里出現(xiàn)2次,剩下的則不會出現(xiàn)。
- 總格子數(shù) % icons個數(shù)x2, 得到剩下未分配的格子
- 未分配格子 / 2, 就是需要隨機從icons中取出的元素個數(shù)n,這個n一定小于icons的個數(shù)
- 從icons中隨機取n個數(shù),可以采用每取一個數(shù),將該數(shù)從原集合刪除,重復n次的方法
- 將得到的n個數(shù)x2,往W里添加
第(3)條是不是聽起來很耳熟,好像前面做過,沒錯就是前面寫的createRandomList函數(shù),W集合變成了icons,total變成了需要的個數(shù)n。
// 剩下未分配的格子個數(shù) const lastCount = total % (icons.length * 2) // 從icons中隨機獲取n個數(shù) const lastList = createRandomList(icons, lastCount / 2) W.push(...lastList, ...lastList)
合在一起就是就是創(chuàng)建W的方法:
function createW(icons: string[], total: number) {
const times = Math.floor(total / (icons.length * 2))
const lastCount = total % (icons.length * 2)
const W = []
for (let index = 0; index < times; index++)
W.push(...icons, ...icons)
const lastList = createRandomList(icons, lastCount / 2)
W.push(...lastList, ...lastList)
return W
}生成最終數(shù)組
完整的生成隨機數(shù)組代碼:
function createW(icons: string[], total: number) {
const times = Math.floor(total / (icons.length * 2))
const lastCount = total % (icons.length * 2)
const W = []
for (let index = 0; index < times; index++)
W.push(...icons, ...icons)
const lastList = createRandomList(icons, lastCount / 2)
W.push(...lastList, ...lastList)
return W
}
function createRandomList(W: string[], total: number, fn: (<T>(icon: string, index?: number) => T) = icon => icon) {
const list: any[] = []
function it(time: number): any {
if (time === 0) return list
const randomNum = Math.floor(Math.random() * (W.length))
list.push(fn(W[randomNum], total-time))
W.splice(randomNum, 1)
return it(--time)
}
return it(total)
}
// ['a', 'b', 'c', "d"] => ['c', 'd'...x15...'b', 'c', 'a']
createRandomList(createW(icons, total), total)
點擊事件
亂序的隨機數(shù)組有了,點一點還不簡單嗎! 先讓生成的數(shù)組屬性更豐富一些,來幫助我們展示內容。
type CardItem = { icon: string; isDel: boolean; isSelect: boolean, index: number }
let list: CardItem[] = []
// isSelect屬性判斷是否翻轉,isDel屬性判斷是否已經消除,icon屬性標注元素屬性,index用來快速找到點擊元素位于數(shù)組中的位置
list = createRandomList(createW(icons, total), total, (icon: string, index) => ({ icon, isDel: false, isSelect: false, index }))
這下可以用生成的數(shù)組去展示了。接下來我們寫個點擊事件,接收參數(shù)是點擊的數(shù)組元素:
// isLock用來鎖定動畫完成前不能進行別的操作
function handlerTap(card: CardItem) {
if (isLock) return
list[card.index].isSelect = true
const selectors = list.filter(item => item.isSelect && !item.isDel)
// 假如選擇元素<2,直接返回,不走之后流程
if (selectors.length <= 1) return
isLock = true
const [item1, item2] = selectors
// 翻轉動畫完成后進行操作
setTimeout(() => {
// 如果選擇的元素相同,則消除屬性等于true
if (item1.icon === item2.icon) {
list[item1.index].isDel = true
list[item2.index].isDel = true
}
//將所有卡牌翻轉過背面
list = list.map(item => ({...item, isSelect: false}))
isLock = false
// 判斷是否所有卡牌都已經翻轉完成
if (list.every(item => item.isDel)) console.log( "your win!")
}, 800)
}完整代碼
100行整)。
<script lang="ts">
type CardItem = { icon: string; isDel: boolean; isSelect: boolean, index: number }
const icons = ['a', 'b', 'c', "d"]
const total = 20
let list: CardItem[] = []
let isLock = false
function handlerTap(card: CardItem) {
if (isLock) return
list[card.index].isSelect = true
const selectors = list.filter(item => item.isSelect && !item.isDel)
if (selectors.length <= 1) return
isLock = true
const [item1, item2] = selectors
setTimeout(() => {
if (item1.icon === item2.icon) {
list[item1.index].isDel = true
list[item2.index].isDel = true
}
list = list.map(item => ({...item, isSelect: false}))
isLock = false
if (list.every(item => item.isDel)) console.log( "your win!")
}, 800)
}
function createW(icons: string[], total: number) {
const times = Math.floor(total / (icons.length * 2))
const lastCount = total % (icons.length * 2)
const W = []
for (let index = 0; index < times; index++)
W.push(...icons, ...icons)
const lastList = createRandomList(icons, lastCount / 2)
W.push(...lastList, ...lastList)
return W
}
function createRandomList(W: string[], total: number, fn: (<T>(icon: string, index?: number) => T) = icon => icon) {
const list: any[] = []
function it(time: number): any {
if (time === 0) return list
const randomNum = Math.floor(Math.random() * (W.length))
list.push(fn(W[randomNum], total-time))
W.splice(randomNum, 1)
return it(--time)
}
return it(total)
}
list = createRandomList(createW(icons, total),
total,
(icon: string, index) => ({ icon, isDel: false, isSelect: false, index }))
</script>
<div class="game-box">
{#each list as item}
<div class="grid">
{#if !item.isDel}
<div class="card {item.isSelect && 'select'}" on:click="{() => handlerTap(item)}">
<div class="top">{item.icon}</div>
</div>
{/if}
</div>
{/each}
</div>
<style lang="less">
.game-box{
margin: 10px auto 0;
width: 90vw;
height: 80vh;
display: grid;
grid-template-columns: repeat(4, calc(100% / 4 - 3px));
grid-template-rows: repeat(5, calc(100% / 5 - 3px));
grid-row-gap:3px;
grid-column-gap: 3px;
.card{
height: 100%;
width: 100%;
box-sizing: border-box;
position: relative;
transform-style: preserve-3d;
transform: rotateY(180deg);
transition: all 600ms;
background: pink;
&.select {
transform: rotateY(0);
}
.top{
height: 100%;
width: 100%;
position: absolute;
top: 0;
left: 0;
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
background: white;
border: 2px solid #b6a6dc;
transform: translateZ(1px);
}
}
}
</style>以上就是基于JavaScript編寫一個翻卡游戲的詳細內容,更多關于JavaScript翻卡游戲的資料請關注腳本之家其它相關文章!
相關文章
javascript動態(tài)添加樣式(行內式/嵌入式/外鏈式等規(guī)則)
添加CSS的方式有行內式、嵌入式、外鏈式、導入式,下面為大家詳細介紹下javascript動態(tài)添加以上樣式規(guī)則的方法,感興趣的朋友可以參考下哈2013-06-06
easyUI實現(xiàn)(alert)提示框自動關閉的實例代碼
下面小編就為大家?guī)硪黄猠asyUI實現(xiàn)(alert)提示框自動關閉的實例代碼。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-11-11
js和jquery判斷數(shù)據(jù)類型的4種方法總結
這篇文章主要給大家介紹了關于js和jquery判斷數(shù)據(jù)類型的4種方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-08-08

