在以前的win7
系統中,自帶的小游戲中,有一款游戲叫掃雷,效果如下圖:
掃雷
html中的結構代碼如下:
<select name="mineNum">
<option value="0">請選擇雷的數量</option>
<option value="10-8">10個雷</option>
<option value="40-16">40個雷</option>
<option value="99-32">99個雷</option>
</select>
剩余雷數量:<span class="mineNum">0</span>
js邏輯代碼中,預置兩個工具函數:
// 設置樣式的函數
function setStyle(ele, styleObj){
for(var attr in styleObj){
ele.style[attr] = styleObj[attr];
}
}
// 獲取隨機數的函數
function getRandom(a,b=0){
var max = a;
var min = b;
if(a<b){
max = b;
min = a;
}
return Math.floor(Math.random() * (max - min)) + min;
}
首先要根據選中的雷的數量,來創建小盒子:
// 獲取標簽
var mineSelect = document.querySelector('[name="mineNum"]');
var mineNumBox = document.querySelector('.mineNum');
// 獲取掃雷游戲需要的列的數量
mineSelect.onchange = function(){
// 獲取當前選中的option的value
var value = this.value;
if(value === '0'){
alert("請選擇雷的數量")
return false;
}
// 從中獲取到類的數量和掃雷需要的列的數量
var mineNum = +value.split('-')[0]
var col = +value.split('-')[1]
// 如果頁面中已經有掃雷游戲了,就將之前的刪除
var mine = document.querySelector('.mine');
if(mine){
document.body.removeChild(mine)
}
// 調用創建掃雷游戲的函數
createMine(mineNum,col)
}
// 創建掃雷游戲的函數
function createMine(mineNum,col){
// 根據列的數量創建大盒子
var box = document.createElement('div')
// 設置類名
box.className = 'mine';
document.body.appendChild(box)
// 不能選中box中的內容
box.onselectstart = function(){
return false
}
// 設置樣式
setStyle(box,{
border:"3px solid #00f",
position:"relative",
width:col*20 + 'px',
height:col*20 + 'px',
})
// 在大盒子中創建小盒子
for(var i=0;i<Math.pow(col,2);i++){
var div = document.createElement('div')
box.appendChild(div)
setStyle(div,{
width:"18px",
height:"18px",
border:"1px solid #fff",
position:"absolute",
left:i%col*20+'px',
top:Math.floor(i/col)*20 + 'px',
backgroundColor:"#aaa"
})
}
// 調用隨機設置雷的函數
var indexArr = setMine(box,mineNum,col)
// 調用計算雷數量的函數
countMine(box,col)
// 點擊小div開始掃
clearance(box,col,indexArr)
}
設置雷的函數如下:
// 設置隨機雷的函數
function setMine(ele,num,col){
// 在ele中創建num個雷
// 定義數組用來存放是雷的小div的下標
var indexArr = []
for(var i=0;i<num;i++){
// 獲取隨機下標
var randomIndex = getRandom(ele.children.length)
// 判斷隨機下標是否在數組中
var index = indexArr.indexOf(randomIndex)
// 如果這個隨機下標不在數組中,就將這個隨機下標放當數組中
if(index<0){
indexArr.push(randomIndex)
}else{
// 這次循環作廢 - 重新創建隨機下標 - 要保證數組中隨機下標的個數一定是雷的數量
i--
}
}
// indexArr中存放的是所有 是雷的div的下標
// 給所有是雷的div做特殊的標記
for(var i=0;i<indexArr.length;i++){
ele.children[indexArr[i]].mine = true;
// ele.children[indexArr[i]].style.backgroundColor = 'red';
}
// 更改頁面中雷的數量
mineNumBox.innerText = num
return indexArr
}
根據設置好的雷,給每個小div計算周圍雷的數量:
// 計算雷數量的函數
function countMine(ele,col){
// 遍歷每個小div,計算周圍雷的數量
for(var i=0;i<ele.children.length;i++){
// 如果當前div是雷就跳過
if(ele.children[i].mine){
continue
}
// 獲取周圍所有div的下標的數組
var arr = getIndexArr(i,col);
// 定義當前div周圍雷的數量變量
var num = 0
// 遍歷周圍的div計算
for(var j=0;j<arr.length;j++){
if(ele.children[i+arr[j]].mine){
num++
}
}
ele.children[i].num = num;
// ele.children[i].innerText = num;
}
}
其中獲取周圍div的下標數組的代碼如下:
// 獲取每個div周圍的div下標的數組
function getIndexArr(index,col){
// 定義周圍的div下標的數組
var arr = [1,-1,col,-col,col+1,col-1,-col-1,-col+1];
if(index<col){
arr = [-1,1,col,col-1,col+1];
}
// 如果是最后1行,周圍只有5個div - arr數組中就應該只有5個下標
if(Math.floor(index/col) === col-1){
arr = [-1,1,-col,-col-1,-col+1];
}
// 如果是第1列,周圍只有5個div - arr數組中就應該只有5個下標
if(index%col === 0){
arr = [col,col+1,1,-col,-col+1];
}
// 如果是最后1列,周圍只有5個div - arr數組中就應該只有5個下標
if((index+1)%col === 0){
arr = [-col-1,-col,-1,col-1,col];
}
// 如果是左上角的div,周圍只有3個div - arr數組中就應該只有3個下標
if(index===0){
arr = [1,col,col+1];
}
// 如果是左下角的div,周圍只有3個div - arr數組中就應該只有3個下標
if(index===(col-1)*col){
arr = [1,-col,-col+1];
}
// 如果是右下角的div,周圍只有3個div - arr數組中就應該只有3個下標
if(index===col*col-1){
arr = [-1,-col,-col-1];
}
// 如果是右上角的div,周圍只有3個div - arr數組中就應該只有3個下標
if(index===col-1){
arr = [-1,col,col-1];
}
return arr
}
再下來就可以開始玩游戲了,開始掃雷的代碼如下:
// 點擊小div開始掃的函數
function clearance(ele,col,indexArr){
// 遍歷所有小div綁定事件
for(let i=0;i<ele.children.length;i++){
// 單擊事件 - 如果不是雷,就將周圍雷的數量顯示出來 - 如果周圍沒有雷就不顯示數量,繼續將周圍的div點開
ele.children[i].onclick = function(){
if(!this.mine){
// 如果點擊的不是雷,就打開
openNow(this,i,col,ele)
}else{
alert("GAME OVER");
// 如果點擊的div是雷,就將所有類引爆,并結束游戲
for(var j=0;j<indexArr.length;j++){
ele.children[indexArr[j]].style.backgroundColor = 'red';
}
}
}
// 右擊事件,標記雷
ele.children[i].oncontextmenu = function(){
// 將雷標紅
this.style.backgroundColor = 'red';
mineNumBox.innerText = mineNumBox.innerText-1
// 阻止默認行為
return false;
}
}
}
打開當前div的函數如下:
// 打開當前小div,設置不同的背景顏色,并判斷是否需要遞歸打開
function openNow(nowEle,i,col,ele){
// 給已經打開的div做標記
nowEle.open = true;
// 設置打開的div的背景顏色
nowEle.style.backgroundColor = '#eee';
// 如果數量是0就繼續打開
if(nowEle.num === 0){
// 繼續打開周圍的div
open(ele,i,col)
}else{
// 如果數量不是0就顯示數量
nowEle.innerText = nowEle.num;
nowEle.style.textAlign = 'center'
nowEle.style.lineHeight = '20px'
nowEle.style.fontSize = '12px'
nowEle.style.color = '#666'
}
}
如果當前div周圍雷的數量為0,就將周圍的div也打開,需要遞歸,函數如下:
// 繼續打開周圍div的函數
function open(ele,index,col){
// 遍歷周圍的div - 如果雷的數量不是0,就顯示數量,如果雷的數量是雷就繼續遞歸打開
var arr = getIndexArr(index,col);
// 遍歷數組,判斷周圍div是否打開
for(var i=0;i<arr.length;i++){
if(ele.children[index+arr[i]].open){
continue;
}else{
openNow(ele.children[index+arr[i]],index+arr[i],col,ele)
}
}
}