原生JS实现简易扫雷游戏

Posted 听雪拨弦

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了原生JS实现简易扫雷游戏相关的知识,希望对你有一定的参考价值。

注:使用了Font Awesome 字体图标库,使用前请下载引入(下载地址

<!DOCTYPE html>
<html lang="zh">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>扫雷小游戏2.2</title>
    <link rel="stylesheet" href="./css/font-awesome.min.css">
    <style>
        * {
            user-select: none;
        }

        .mineBox {
            width: 900px;
            height: 600px;
            margin: 0 auto;
            border: 3px solid gray;
            position: relative;
            color: black;
        }

        .red {
            font-size: 22px;
            color: #B22222;
        }

        .black {
            font-size: 22px;
            color: black;
        }

        .difficulty {
            width: 900px;
            text-align: center;
            margin: 0 auto 15px;
        }

        button {
            width: 60px;
            height: 25px;
        }

        .info {
            position: fixed;
            top: 100px;
            left: 190px;
        }

        input {
            width: 60px;
            height: 40px;
            margin-left: 2px;
            margin-top: 10px;
            text-align: center;
            font-weight: bold;
            font-size: 18px;
        }

        #times {
            font-size: 16px;
            font-weight: none;
        }

        .success {
            position: absolute;
            width: 120px;
            height: 40px;
            text-align: center;
            line-height: 40px;
            top: -160px;
            bottom: 0;
            left: 0;
            right: 0;
            margin: auto;
            background: rgba(255, 255, 255, 0.4);
            z-index: 1000;
            border-radius: 25px;
            display: none;
        }
    </style>
</head>

<body>
    <div class="success">
        恭喜通关
    </div>
    <div class="difficulty">
        <b>难度选择:</b>&nbsp;<b style="color: #FF00FF">Lunatic</b><br>
        <button class="Easy">Easy</button>
        <button class="Normal">Normal</button>
        <button class="Hard">Hard</button>
        <button class="Lunatic">Lunatic</button>
        <button class="Extra">Extra</button>
    </div>
    <div class="mineBox"></div>
    <div class="info">
        地雷:
        <input type="text" readonly id="txtB"><br>
        旗帜:
        <input type="text" readonly id="txtF"><br>
        时间:
        <input type="text" readonly id="times" value="0"><br>
    </div>
    <script>
        class Mining {
            constructor() {
                // 获取存放小盒子的节点
                this.mineBox = document.getElementsByClassName('mineBox')[0];
                // 定义一个用来存放雷div的下标的数组
                this.arr = [];
                // 获取存放按钮的节点
                this.btnList = document.querySelectorAll('.difficulty button');
                // 获取显示难度的节点
                this.diff = document.getElementsByTagName('b')[1];
                // 获取显示雷数量的节点
                this.txtBomb = document.getElementById('txtB');
                // 获取显示旗子数量的节点
                this.txtFlag = document.getElementById('txtF');
                // 获取显示时间的节点
                this.txtTime = document.getElementById('times');
                // 定义小盒子的默认宽高
                this.sDivW = 30;
                this.sDivH = 30;
                // 定义雷的默认数量
                this.bombSum = 120;
                // 定义旗子的默认数量
                this.flagSum = this.bombSum;
                // 将雷和旗子的数量显示到input框内
                this.txtBomb.value = this.bombSum;
                this.txtFlag.value = this.flagSum;
                // 设置标记正确后的变量
                this.success = 0;
                // 设置游戏结束状态
                this.endGame = false;
                // 定义计时器&计时器状态
                this.times = '';
                this.timeStatus = true;
                // 默认执行一次
                this.init();
                // 给按钮绑定点击事件
                this.btnList.forEach(v => {
                    v.addEventListener('click', () => {
                        switch (v.className) {
                            case 'Easy':
                                this.clear();
                                this.mineBox.style.width = '500px';
                                this.mineBox.style.height = '500px';
                                this.sDivW = 50;
                                this.sDivH = 50;
                                this.bombSum = 10;
                                this.flagSum = this.bombSum;
                                this.txtBomb.value = this.bombSum;
                                this.txtFlag.value = this.flagSum;
                                this.init();
                                this.diff.innerText = 'Easy';
                                this.diff.style.color = '#00FF00';
                                break;
                            case 'Normal':
                                this.clear();
                                this.mineBox.style.width = '600px';
                                this.mineBox.style.height = '600px';
                                this.sDivW = 50;
                                this.sDivH = 50;
                                this.bombSum = 15;
                                this.flagSum = this.bombSum;
                                this.txtBomb.value = this.bombSum;
                                this.txtFlag.value = this.flagSum;
                                this.init();
                                this.diff.innerText = 'Normal';
                                this.diff.style.color = '#4169E1';
                                break;
                            case 'Hard':
                                this.clear();
                                this.mineBox.style.width = '800px';
                                this.mineBox.style.height = '600px';
                                this.sDivW = 50;
                                this.sDivH = 50;
                                this.bombSum = 30;
                                this.flagSum = this.bombSum;
                                this.txtBomb.value = this.bombSum;
                                this.txtFlag.value = this.flagSum;
                                this.init();
                                this.diff.innerText = 'Hard';
                                this.diff.style.color = '#FF0000';
                                break;
                            case 'Lunatic':
                                this.clear();
                                this.mineBox.style.width = '900px';
                                this.mineBox.style.height = '600px';
                                this.sDivW = 30;
                                this.sDivH = 30;
                                this.bombSum = 120;
                                this.flagSum = this.bombSum;
                                this.txtBomb.value = this.bombSum;
                                this.txtFlag.value = this.flagSum;
                                this.init();
                                this.diff.innerText = 'Lunatic';
                                this.diff.style.color = '#FF00FF';
                                break;
                            case 'Extra':
                                this.clear();
                                this.mineBox.style.width = '900px';
                                this.mineBox.style.height = '600px';
                                this.sDivW = 30;
                                this.sDivH = 30;
                                this.bombSum = 180;
                                this.flagSum = this.bombSum;
                                this.txtBomb.value = this.bombSum;
                                this.txtFlag.value = this.flagSum;
                                this.init();
                                this.diff.innerText = 'Extra';
                                this.diff.style.color = '#A020F0';
                                break;
                            default:
                                throw console.error('报错了...');
                                break;
                        }
                    })
                });
            }
            clear() {
                //清空大盒子
                this.mineBox.innerHTML = '';
                //清空存放div的数组
                this.arr = [];
                //重置时间
                this.txtTime.value = '0';
                //清除定时器
                clearInterval(this.times);
                //设置定时器状态
                this.timeStatus = true;
                // 设置游戏结束状态
                this.endGame = false;
                //清除标记正确后的变量
                this.succefss = 0;
            }
            init() {
                // 动态创建小div放在box里面
                this.create(this.sDivW, this.sDivH);
                // 标记地雷
                this.markBomb();
                // 点击小盒子
                this.blockClick();
            }
            create(mineW, mineH) {
                this.mineN = this.mineBox.clientWidth * this.mineBox.clientHeight / (mineW * mineH);
                this.mineCol = this.mineBox.clientWidth / mineW;
                this.mineRow = this.mineBox.clientHeight / mineH;
                // 定义一个数组,将周围div的下标存起来
                this.arr2 = [-(this.mineCol + 1), -this.mineCol, -(this.mineCol - 1), -1, 1, this.mineCol - 1, this.mineCol, (this.mineCol + 1)];
                for (let i = 0; i < this.mineN; i++) {
                    let block = document.createElement('div');
                    setStyle(block, {
                        width: mineW - 2 + "px",
                        height: mineH - 2 + "px",
                        backgroundColor: "rgb(141,162,155)",
                        border: "1px solid #fff",
                        position: "absolute",
                        left: (i % this.mineCol) * mineW + "px",
                        top: parseInt(i / this.mineCol) * mineH + "px",
                        textAlign: "center",
                        lineHeight: mineH + "px"
                    })
                    this.mineBox.appendChild(block);
                    //判断是不是雷
                    block.mine = false;
                    //判断小方块是否被打开
                    block.show = false;
                    //判断插旗状态
                    block.status = 0;
                }
            }
            markBomb() {
                // 标记雷
                for (let i = 0; i < this.bombSum; i++) {
                    // 获取随机下标
                    let index = Math.floor(Math.random() * this.mineN)
                    // 判断数组中是否已经有了这个下标
                    if (this.arr.indexOf(index) < 0) this.arr.push(index); else i--;
                }

                for (let i = 0; i < this.arr.length; i++) {
                    this.mineBox.children[this.arr[i]].mine = true
                }
                for (var i = 0; i < this.mineBox.children.length; i++) {
                    // 如果当前这个div是雷就不统计了
                    if (this.mineBox.children[i].mine) {
                        continue;
                    }
                    // this.mineBox.children[i] // 每个div
                    // 查看当前div周围的所有div
                    // 定义周围雷数量的变量
                    let bombN = 0
                    for (var j = 0; j < this.arr2.length; j++) {
                        // 考虑最上面一行 - 不能-(this.mineCol+1) 不能-this.mineCol 不能-(this.mineCol-1)
                        if (i < this.mineCol && (this.arr2[j] === -(this.mineCol + 1) || this.arr2[j] === -this.mineCol || this.arr2[j] === -(this.mineCol - 1))) {
                            continue;
                        }
                        // // 最左边一行过滤掉
                        if (i % this.mineCol === 0 && (this.arr2[j] === -(this.mineCol + 1) || this.arr2[j] === -1 || this.arr2[j] === (this.mineCol - 1))) {
                            continue;
                        }
                        // 最下面一行
                        if (parseInt(i / this.mineCol) === this.mineRow - 1 && (this.arr2[j] === (this.mineCol - 1) || this.arr2[j] === this.mineCol || this.arr2[j] === (this.mineCol + 1))) {
                            continue;
                        }
                        // 最右边一行
                        if (i % this.mineCol === this.mineCol - 1 && (this.arr2[j] === -(this.mineCol - 1) || this.arr2[j] === 1 || this.arr2[j] === (this.mineCol + 1))) {
                            continue;
                        }
                        if (this.mineBox.children[i + (this.arr2[j])].mine) {
                            bombN++
                        }

                    }
                    // 将雷的数量放在div中
                    this.mineBox.children[i].bombN = bombN
                }
            }
            blockClick() {
                for (let i = 0; i < this.mineBox.children.length; i++) {
                    this.mineBox.children[i].index = i;
                    let that = this;
                    this.mineBox.children[i].onclick = function () {
                        //如果计时器未开启 则开启计时器 (仅在第一次点击时开启定时器)
                        if (that.timeStatus) {
                            that.txtTime.value = '1';
                            that.timer();
                            that.timeStatus = false;
                        }
                        if (this.mine) {
                            // 如果点击到雷了
                            for (let j = 0; j < that.mineBox.children.length; j++) {
                                //清除计时器
                                clearInterval(that.times);
                                // 设置游戏结束状态
                                that.endGame = true;
                                // 给所有不是雷的div设置内容显示,雷的数量
                                that.mineBox.children[j].innerText = that.mineBox.children[j].bombN ? that.mineBox.children[j].bombN : '';
                                that.mineBox.children[j].style.backgroundColor = 'rgb(210,222,238)';
                            }
                            // 将所有是雷的div设置为红色
                            for (let j = 0; j < that.arr.length; j++) {
                                that.mineBox.children[that.arr[j]].style.backgroundColor = 'red';
                                that.mineBox.children[that.arr[j]].innerHTML = '<i class="fa fa-bomb black">';
                            }
                        } else {
                            that.openAround(this.index)
                        }
                    }
                    this.mineBox.children[i].oncontextmenu =
                        function () {
                            // 如果小盒子已经是打开的状态 则跳出函数
                            if (this.show) return false;
                            // 如果游戏已经结束了 则跳出函数
                            if (that.endGame) return false;
                            if (this.status == 0) {
                                this.innerHTML = '<i class="fa fa-flag red">';
                                this.status = 1;
                                that.flagSum--;
                                that.txtFlag.value = that.flagSum;
                                if (this.mine) {
                                    that.success++;
                                    if (that.success == that.bombSum) {
                                        //清除计时器
                                        clearInterval(that.times);
                                        //设置通关状态
                                        that.endGame = true;
                                        //显示通关信息
                                        document.getElementsByClassName('success')[0].style.display = 'block';
                                        //设置延时2秒后关闭
                                        setTimeout(() => {
                                            document.getElementsByClassName('success')[0].style.display = 'none';
                                        }, 2000);
                                    }
                                }
                                return false;
                            }
                            if (this.status == 1) {
                                this.innerHTML = '<i class="fa fa-question black">';
                                this.status = 2;
                                return false;
                            }
                            if (this.status) {
                                if (this.mine) {
                                    that.success--;
                                }
                                this.innerHTML = '';
                                this.status = 0;
                                that.flagSum++;
                                that.txtFlag.value = that.flagSum;
                                return false;
                            }
                        }
                }
            }
            // 打开周围div的递归函数
            openAround(index) {
                this.mineBox.children[index].innerText = this.mineBox.children[index].bombN
                this.mineBox.children[index].style.backgroundColor = 'rgb(210,222,238)'
                this.mineBox.children[index].show = true
                if (this.mineBox.children[index].bombN === 0) {
                    this.mineBox.children[index].innerText = '';
                    for (let j = 0; j < this.arr2.length; j++) {
                        if (index < this.mineCol && (this.arr2[j] === -(this.mineCol + 1) || this.arr2[j] === -this.mineCol || this.arr2[j] === -(this.mineCol - 1))) {
                            continue;
                        }
                        // // 最左边一行过滤掉
                        if (index % this.mineCol === 0 && (this.arr2[j] === -(this.mineCol + 1) || this.arr2[j] === -1 || this.arr2[j] === (this.mineCol - 1))) {
                            continue;
                        }
                        // 最下面一行
                        if (parseInt(index / this.mineCol) === this.mineRow - 1 && (this.arr2[j] === (this.mineCol - 1) || this.arr2[j] === this.mineCol || this.arr2[j] === (this.mineCol + 1))) {
                            continue;
                        }
                        // 最右边一行
                        if (index % this.mineCol === this.mineCol - 1 && (this.arr2[j] === -(this.mineCol - 1) || this.arr2[j] === 1 || this.arr2[j] === (this.mineCol + 1))) {
                            continue;
                        }
                        if (this.mineBox.children[index + this.arr2[j]].show) {
                            continue
                        }
                        this.openAround(index + this.arr2[j])
                    }
                }
            }
            // 设置计时器函数
            timer() {
                this.times = setInterval(() => {
                    this.txtTime.value = (this.txtTime.value - 0) + 1;
                }, 1000);
            }
        }
        new Mining;
        //封装设置样式的方法
        function setStyle(tag, styleObj) {
            for (var attr in styleObj) {
                tag.style[attr] = styleObj[attr];
            }
        }
    </script>
</body>

</html>

 

以上是关于原生JS实现简易扫雷游戏的主要内容,如果未能解决你的问题,请参考以下文章

原生JS实现简易扫雷游戏

原生JS实现简易扫雷游戏

原生JS实现简易扫雷游戏

简易扫雷游戏的详解

原生 JavaScript 实现扫雷 (分析+代码实现)

WINAPI实现简易扫雷游戏