在以前的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)
}
}
}