JS原生 扫雷游戏

运行效果

效果图

操作方法

  • 单击绿色环形按钮即可开局,用任何工具首次点击雷区不会出现游戏失败情况;
  • 点击左侧图标选择工具,选择不同工具点击雷区效果不同;
  • 红旗图标为windows扫雷右击,普通箭头图标为windows扫雷左击,金色箭头图标为windows扫雷左右同时;
  • 点击右侧太阳或月亮可切换显示模式。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
     
     
            margin: 0;
            padding: 0;
            -webkit-touch-callout: none;
            -webkit-user-select: none;
            -moz-user-select: none;
            -ms-user-select: none;
            -o-user-select: none;
            user-select: none;
        }
        html, body {
     
     
            width: 100%;
            height: 100%;
        }
        #form {
     
     
            display: inline-block;
        }
        #form table {
     
     
            border-spacing: 2px;
        }
        #form td {
     
     
            width: 2vw;
            height: 2vw;
            text-align: center;
            vertical-align: middle;
            position: relative;
        }
        #form td svg {
     
     
            position: absolute;
            top: 0;
            left: 0;
        }
        .visited {
     
     
            background-color: transparent;
        }
        tr {
     
     
            width: 100%;
        }
        tr svg {
     
     
            display: table-cell;
        }
        *[name='btn'] {
     
     
            width: 100%;
        }
        .big {
     
     
            transform: scale(1.5);
        }
        .btn-box {
     
     
            width: 100px;
        }
        #tools td {
     
     
            background-color: transparent;
        }
        #tools td div {
     
     
            margin: 2vw;
            width: 6vw;
            height: 6vw;
            position: relative;
            overflow: show;
        }
        #tools td div svg {
     
     
            position: absolute;
            margin: auto;
        }
        .light {
     
     
            background-color: white;
        }
        .light * {
     
     
            color: black;
        }
        .dark {
     
     
            background-color: #040404;
        }
        .dark * {
     
     
            color: white;
        }
        .unclc {
     
     
            background-color: #4444;
            transition: all 3s;
        }
        .dark .unclc {
     
     
            background-color: #4444;
            transition: all 3s;
        }
        .unclc:hover {
     
     
            background-color: #4448;
            transition: all 0s;
        }
        .dark .unclc:hover {
     
     
            background-color: #4448;
            transition: all 0s;
        }
        table {
     
     
            text-align: center;
        }
        .mine {
     
     
            fill: #fff;
        }
        .dark .mine {
     
     
            fill: #c33;
        }
        .died {
     
     
            background-color: #f009;
        }
        .dark .died {
     
     
            background-color: #fff8;
        }
        .clock {
     
     
            fill: #000;
        }
        .dark .clock {
     
     
            fill: #f44;
        }
        .restart:hover * {
     
     
            animation: rot 1.25s linear infinite;
            -ms-animation: rot 1.25s linear infinite;
            -webkit-animation: rot 1.25s linear infinite;
            -moz-animation: rot 1.25s linear infinite;
            -o-animation: rot 1.25s linear infinite;
        }
        .restart:active * {
     
     
            animation: rot 1.25s linear infinite;
            -ms-animation: rot 1.25s linear infinite;
            -webkit-animation: rot 1.25s linear infinite;
            -moz-animation: rot 1.25s linear infinite;
            -o-animation: rot 1.25s linear infinite;
        }
        @keyframes rot {
     
     
            from {
     
     
                transform-origin: 50% 50%;
            }
            to {
     
     
                transform-origin: 50% 50%;
                transform: rotate(360deg);
            }
        }
        @-ms-keyframes rot {
     
     
            from {
     
     
                transform-origin: 50% 50%;
            }
            to {
     
     
                transform-origin: 50% 50%;
                transform: rotate(360deg);
            }
        }
        @-webkit-keyframes rot {
     
     
            from {
     
     
                transform-origin: 50% 50%;
            }
            to {
     
     
                transform-origin: 50% 50%;
                transform: rotate(360deg);
            }
        }
        @-moz-keyframes rot {
     
     
            from {
     
     
                transform-origin: 50% 50%;
            }
            to {
     
     
                transform-origin: 50% 50%;
                transform: rotate(360deg);
            }
        }
        @-o-keyframes rot {
     
     
            from {
     
     
                transform-origin: 50% 50%;
            }
            to {
     
     
                transform-origin: 50% 50%;
                transform: rotate(360deg);
            }
        }
    </style>
</head>
<body class="light">
    <table class="win">
        <tr>
            <td id="timer">
            </td>
        </tr>
        <tr>
            <td id="box">
                <table id="tools">
                    <tr>
                        <td class="btn-box">
                            <div data-id="1" name="btn" ontap="setMode(1)" onclick="setMode(1)">
                                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
                                    <path d="M50,13A37,37,0,1,1,13,50,37.04192,37.04192,0,0,1,50,13M50,0a50,50,0,1,0,50,50A50,50,0,0,0,50,0Z" fill="#888" opacity="0.5" />
                                    <polygon points="50 99.037 40.926 100 15.411 4.777 24.118 2.444 50 99.037" fill="gray" />
                                    <polygon points="61.14 42.93 40.68 64.24 24.12 2.44 50.29 2.44 61.14 42.93" fill="#b22" />
                                    <polygon points="75.9 21.31 56.18 41.86 45.33 1.37 70.56 1.37 75.9 21.31" fill="#d33" />
                                    <polygon points="61.14 42.93 56.182 41.86 59.834 38.055 61.14 42.93" fill="#c22" />
                                    <polygon points="93.3 0 74.16 19.94 68.82 0 93.3 0" fill="#f44" />
                                    <polygon points="75.234 18.822 75.9 21.31 74.16 19.94 75.234 18.822" fill="#e33" />
                                </svg>
                            </div>
                        </td>
                    </tr>
                    <tr>
                        <td class="btn-box">
                            <div data-id="0" name="btn" ontap="setMode(0)" onclick="setMode(0)">
                                <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100">
                                    <defs>
                                        <linearGradient id="a" x1="50" y1="6.42815" x2="50" y2="93.57185" gradientUnits="userSpaceOnUse">
                                            <stop offset="0" stop-color="#f6e9b4" />
                                            <stop offset="1" stop-color="#ad8a50" />
                                        </linearGradient>
                                    </defs>
                                    <path d="M50,13A37,37,0,1,1,13,50,37.04192,37.04192,0,0,1,50,13M50,0a50,50,0,1,0,50,50A50,50,0,0,0,50,0Z" fill="#888" opacity="0.5" />
                                    <polygon points="93.572 36.591 6.428 6.428 36.591 93.572 51.237 61.812 82.997 93.572 93.572 82.997 61.812 51.237 93.572 36.591" stroke="#4b4239"
                                        stroke-miterlimit="10" stroke-width="4" fill="url(#a)" />
                                    <polygon
                                        points="81.838 28.644 70.614 34.545 72.758 22.047 63.677 13.195 76.226 11.372 81.838 0 87.451 11.372 100 13.195 90.919 22.047 93.063 34.545 81.838 28.644"
                                        fill="#fbb03b" />
                                </svg>
                            </div>
                        </td>
                    </tr>
                    <tr>
                        <td class="btn-box">
                            <div data-id="-1" name="btn" ontap="setMode(-1)" onclick="setMode(-1)">
                                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
                                    <path d="M50,13A37,37,0,1,1,13,50,37.04192,37.04192,0,0,1,50,13M50,0a50,50,0,1,0,50,50A50,50,0,0,0,50,0Z" fill="#888" opacity="0.5" />
                                    <polygon points="93.572 36.591 6.428 6.428 36.591 93.572 51.237 61.812 82.997 93.572 93.572 82.997 61.812 51.237 93.572 36.591" fill="#fff"
                                        stroke="#000" stroke-miterlimit="10" stroke-width="4" />
                                </svg>
                            </div>
                        </td>
                    </tr>
                </table>
            </td>
            <td id="form"></td>
            <td>
                <table id="tools">
                    <tr>
                        <td class="btn-box">
                            <div name="btn" ontap="setColor()" onclick="setColor()">
                                <span id="moon" style="display: none;">
                                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
                                        <path d="M72.851,80.4575a39.3,39.3,0,1,1,0-60.915,30.89293,30.89293,0,1,0,0,60.915Z" fill="#fff" />
                                    </svg>
                                </span>
                                <span id="sun">
                                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
                                        <circle cx="50" cy="50" r="30.9" fill="#f7931e" />
                                        <path d="M50,83.45a33.54854,33.54854,0,0,0,11.48-2.02L50,100,38.52,81.43A33.54854,33.54854,0,0,0,50,83.45Z" fill="#f7931e" />
                                        <path d="M50,16.55a33.5471,33.5471,0,0,0-11.48,2.02L50,0,61.48,18.57A33.5471,33.5471,0,0,0,50,16.55Z" fill="#f7931e" />
                                        <path d="M14.64,14.64,35.9,19.66A33.55962,33.55962,0,0,0,19.66,35.9Z" fill="#f7931e" />
                                        <path d="M85.36,85.36,64.1,80.34A33.55937,33.55937,0,0,0,80.34,64.1Z" fill="#f7931e" />
                                        <path d="M18.57,38.52a33.63113,33.63113,0,0,0,0,22.96L0,50Z" fill="#f7931e" />
                                        <path d="M81.43,61.48a33.63113,33.63113,0,0,0,0-22.96L100,50Z" fill="#f7931e" />
                                        <path d="M35.9,80.34,14.64,85.36,19.66,64.1A33.55937,33.55937,0,0,0,35.9,80.34Z" fill="#f7931e" />
                                        <path d="M85.36,14.64,80.34,35.9A33.55962,33.55962,0,0,0,64.1,19.66Z" fill="#f7931e" />
                                    </svg>
                                </span>
                            </div>
                        </td>
                    </tr>
                    <tr>
                        <td class="btn-box">
                            <div name="btn" onclick="restart()">
                                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" class="restart">
                                    <path d="M100,50a50.00249,50.00249,0,1,1-1.42-11.81H61.81L76.16,23.84A36.99578,36.99578,0,1,0,87,50Z" fill="#5a5" opacity="0.7" />
                                </svg>
                            </div>
                        </td>
                    </tr>
                    <tr>
                        <td class="btn-box">
                            <div name="btn" onclick="">
                                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
                                    <path d="M50,13A37,37,0,1,1,13,50,37.04192,37.04192,0,0,1,50,13M50,0a50,50,0,1,0,50,50A50,50,0,0,0,50,0Z" fill="#888" opacity="0" />
                                </svg>
                            </div>
                        </td>
                    </tr>
                </table>
            </td>
        </tr>
    </table>
</body>
</html>
<script>
    const NUM = 0b0001111;
    const MINE = 0b0010000;
    const FLAG = 0b0100000;
    const HIDE = 0b1000000;
    var mode = 0;//-1表示扫雷,1表示标记,0表示自动打开
    function setColor() {
     
     
        let body = document.getElementsByTagName('body')[0];
        if (body.getAttribute('class') == 'light') {
     
     
            body.setAttribute('class', 'dark');
            document.getElementById('sun').setAttribute('style', 'display:none;');
            document.getElementById('moon').setAttribute('style', '');
        } else {
     
     
            body.setAttribute('class', 'light');
            document.getElementById('moon').setAttribute('style', 'display:none;');
            document.getElementById('sun').setAttribute('style', '');
        }
    }
    function setMode(v) {
     
     
        if (mode != v) {
     
     
            mode = v;
            let children = document.getElementsByName('btn');
            for (let i = 0, len = children.length; i < len; i++)
                children[i].setAttribute('class', children[i].getAttribute('data-id') == mode ? 'big' : '');
        }
    }
    setMode(-1);
    var root = document.getElementById('form');
    var settings = new Object();
    function init(width = 30, height = 16, mine = 99) {
     
     
        settings.h = Math.min(Math.max(height, 9), 36);//限制雷区大小
        settings.w = Math.min(Math.max(width, 9), 60);//限制雷区大小
        settings.m = Math.min(settings.w * settings.h * 0.2, Math.max(settings.w * settings.h * 0.12, mine));//限制雷密度
        settings.map = [];//地图
        for (let y = 0; y < settings.h; y++) {
     
     
            settings.map[y] = [];
            for (let x = 0; x < settings.w; x++)
                settings.map[y][x] = HIDE;//隐藏全部方格数据
        }
        settings.playing = false;//游戏初始未开始
        settings.living = true;//玩家初始存活
    }
    function getNeighbours(x, y) {
     
     
        let map = [
            {
     
      x: x, y: y + 1 }, {
     
      x: x, y: y - 1 }, {
     
      x: x + 1, y: y }, {
     
      x: x + 1, y: y + 1 }, {
     
      x: x + 1, y: y - 1 }, {
     
      x: x - 1, y: y }, {
     
      x: x - 1, y: y + 1 }, {
     
      x: x - 1, y: y - 1 }
        ];
        return map;
    }
    function ranMines(x, y) {
     
     
        let map = getNeighbours(x, y);
        map.push({
     
      x: x, y: y });
        for (let i = 0; i < settings.m;) {
     
     //确定所有雷的位置
            let x1 = parseInt(Math.random() * settings.w);
            let y1 = parseInt(Math.random() * settings.h);
            let check = settings.map[y1][x1] & MINE;
            if (!check) {
     
     //确保雷不重复,且不与单击点重合
                let fill = true;
                for (let i1 = 0; i1 < 9; i1++)//防止第一次点雷,第一次安全区域必须成片
                    if (map[i1].x == x1 && map[i1].y == y1) {
     
     
                        fill = false;
                        break;
                    }
                if (fill) {
     
     //确认可以埋地雷
                    settings.map[y1][x1] += MINE;//安放雷
                    i++;
                }
            }
        }
        for (let y = 0; y < settings.h; y++)
            for (let x = 0; x < settings.w; x++) {
     
     
                let val = 0;//方格上数字
                let tmp = [settings.map[y][x + 1], settings.map[y][x - 1]];
                if (settings.map[y + 1]) tmp.push(settings.map[y + 1][x + 1], settings.map[y + 1][x], settings.map[y + 1][x - 1])
                if (settings.map[y - 1]) tmp.push(settings.map[y - 1][x + 1], settings.map[y - 1][x], settings.map[y - 1][x - 1])
                for (let i = 0, len = tmp.length; i < len; i++)
                    if (tmp[i]) {
     
     
                        let t = tmp[i] & MINE;
                        if (t) val++;
                    }
                settings.map[y][x] += val;//标注数字
            }
    }
    function showMine(ele) {
     
     //将一个方格加上图标
        var svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">' +
            '<circle cx="50" cy="50" r="30.9" class="mine"/>' +
            '<polygon points="89.3 50 80.032 42.707 57.293 19.968 50 10.7 42.707 19.968 ' +
            '19.969 42.707 10.7 50 19.969 57.293 42.707 80.032 50 89.3 57.293 80.031 80.032 57.293 89.3 50" class="mine"/>' +
            '<polygon points="77.789 22.211 66.079 23.607 33.922 23.607 22.211 22.211 23.607 33.921 ' +
            '23.608 66.078 22.211 77.789 33.922 76.392 66.079 76.393 77.789 77.789 76.392 66.078 76.393 33.921 77.789 22.211" class="mine"/>' +
            '</svg>';
        ele.innerHTML = svg;
    }
    function showAllMines() {
     
     
        let lines = root.getElementsByTagName("tr");
        for (let linei = 0, l = lines.length; linei < l; linei++) {
     
     
            for (let y = 0; y < settings.h; y++) {
     
     
                let line = root.querySelector("tr[data-y='" + y + "']");//选取一行
                for (let x = 0; x < settings.w; x++) {
     
     
                    let mine = settings.map[y][x] & MINE;
                    if (mine)//此方格有雷
                        showMine(line.querySelector("td[data-x='" + x + "']"));
                }
            }
        }
    }
    function leftClc(ele, x, y) {
     
     
        let b = mode;
        mode = -1;
        let hide = settings.map[y][x] & HIDE;
        let flag = settings.map[y][x] & FLAG;
        if (hide && !flag) {
     
     //只能点击既没有雷也没有点击过的方格
            let mine = settings.map[y][x] & MINE;//是否有雷
            if (mine) {
     
     
                settings.living = false;
                showAllMines(ele);
                ele.setAttribute('class', 'died');
            } else {
     
     
                let num = settings.map[y][x] & NUM;//数字
                settings.map[y][x] -= HIDE;//方格记为已经点击过
                ele.setAttribute('class', 'visited');//改样式
                if (num > 0) ele.innerHTML = num;//方格显示数字但不显示0
                else {
     
     
                    let map = getNeighbours(x, y);
                    for (let i1 = 0; i1 < 8; i1++) {
     
     //遍历周围8个方格
                        let m1 = map[i1] & MINE;
                        let n1 = map[i1] & NUM;
                        if (map[i1].y >= 0 && map[i1].y < settings.h && map[i1].x >= 0 && map[i1].x < settings.w && !(n1 || m1))//展开数字为0且无雷的方格
                            blockClick(undefined, map[i1].x, map[i1].y);
                    }
                }
            }
        }
        mode = b;
    }
    function setFlag(ele, x, y) {
     
     
        var svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="-10 -10 120 120">' +
            '<polygon points="45.935 72.421 27.155 2.333 35.862 0 55.498 73.282 45.935 72.421" fill="#aaa"/>' +
            '<polygon points="35.862 0 100 0 52.421 61.8 35.862 0" fill="#f44" opacity="0.75"/>' +
            '<polygon points="0 100 45.935 72.421 55.498 73.282 100 100 0 100" fill="#fff" opacity="0.75"/>' +
            '</svg>';
        ele.innerHTML = svg;
        if (!(settings.map[y][x] & FLAG))
            settings.map[y][x] += FLAG;
    }
    function rightClc(ele, x, y) {
     
     
        let hide = settings.map[y][x] & HIDE;
        let flag = settings.map[y][x] & FLAG;
        if (hide) {
     
     
            if (flag) {
     
     //取消标记
                ele.innerHTML = '';
                settings.map[y][x] -= FLAG;
            }
            else//标记
                setFlag(ele, x, y);
        }
    }
    function blockClick(element, x, y) {
     
     
        if (!settings.playing) {
     
     
            setMode(-1);
            ranMines(x, y)//设置宽高和雷数
            settings.playing = true;//第一次单击后就开始游戏了
        }
        if (settings.living && x >= 0 && x < settings.w && y >= 0 && y < settings.h) {
     
     
            let ele = element ? element : root.querySelector("tr[data-y='" + y + "']").querySelector("td[data-x='" + x + "']");//如果参数为空则自己找被点击对应元素
            if (mode < 0) {
     
     
                leftClc(ele, x, y);
            } else if (mode > 0) {
     
     
                rightClc(ele, x, y);
            } else {
     
     
                blockDoubleClick(ele, x, y);
            }
        }
    }
    function blockDoubleClick(ele, x, y) {
     
     //左右键按下自动打开 
        let hide = settings.map[y][x] & HIDE; let num = settings.map[y][x] & NUM;//数字 
        if (!hide) {
     
     
            let sel = getNeighbours(x, y);
            //let hideBlock = 0; 
            let flagBlock = 0;
            //let miss = 0; 
            for (let i = 0; i < 8; i++) {
     
     
                if (sel[i].x >= 0 && sel[i].x < settings.w && sel[i].y >= 0 && sel[i].y < settings.h) {
     
     
                    //let mine = settings.map[sel[i].y][sel[i].x] & MINE;//是否有雷 
                    let flag = settings.map[sel[i].y][sel[i].x] & FLAG;//是否未点击过 
                    //let hide = settings.map[sel[i].y][sel[i].x] & HIDE;//是否未点击过 
                    //if (hide) hideBlock++;//计算未点击过的方格数 
                    //else if (mine) miss++; 
                    if (flag) flagBlock++;//计算未点击过的方格数 
                }
            } if (flagBlock >= num) {
     
     
                for (let i = 0; i < 8; i++)
                    if (sel[i].x >= 0 && sel[i].x < settings.w && sel[i].y >= 0 && sel[i].y < settings.h)
                        leftClc(root.querySelector("tr[data-y='" + sel[i].y + "']").querySelector("td[data-x='" + sel[i].x + "']"), sel[i].x, sel[i].y);
                return;
            }
            /*此功能有毒
            if (hideBlock == num && miss == 0) {
                for (let i = 0; i < 8; i++)
                    if (sel[i].x >= 0 && sel[i].x < settings.w && sel[i].y >= 0 && sel[i].y < settings.h) {
                        let hide = settings.map[sel[i].y][sel[i].x] & HIDE;
                        if (hide)
                            setFlag(root.querySelector("tr[data-y='" + sel[i].y + "']").querySelector("td[data-x='" + sel[i].x + "']"), sel[i].x, sel[i].y);
                    }
                return;
            }
            */
        }
    }
    function createGameTable(width = 30, height = 16, mine = 99) {
     
     
        init(30, 16, 99);
        let ret = document.createElement('table');
        for (let y = 0; y < settings.h; y++) {
     
     
            let line = document.createElement('tr');
            line.setAttribute('data-y', y);
            for (let x = 0; x < settings.w; x++) {
     
     
                let box = document.createElement('td');
                box.setAttribute('class', 'unclc');
                box.setAttribute('data-x', x);
                box.addEventListener('click', function () {
     
      blockClick(box, x, y); }, false);
                box.addEventListener('tap', function () {
     
      blockClick(box, x, y); }, false);
                line.appendChild(box);
            }
            ret.appendChild(line);
        }
        return ret;
    }
    function restart() {
     
     
        root.innerHTML = '';
        root.appendChild(createGameTable());
    }
    restart();
</script>

猜你喜欢

转载自blog.csdn.net/dscn15848078969/article/details/111876577
今日推荐