HTML+CSS+JS实现前端扫雷游戏

效果图:

源码如下: 

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>扫雷游戏</title>
    <style>html {
        font-size: 10px;
    }

    body {
        counter-reset: total cleared bombs flagged;
        font-size: 1.6rem;
        font-family: sans-serif;
    }

    H1 {
        font-size: 2rem;
        margin: 1rem auto;
        width: fit-content
    }

    #game {
        overflow: hidden;
        position: relative;
        margin: 0 auto;
        background: lightgrey;
        width: fit-content;
        height: fit-content;
        padding: 1rem;
        border: 0.3rem outset lightgrey;
        border-radius: 1rem;
    }

    .square {
        position: relative;
        float: left;
        display: block;
        width: 2rem;
        height: 2rem;
        border: 1px grey solid;
        background-color: lightgrey;
        text-align: center;
    }

    .square > label,
    .square > .flag {
        position: absolute;
        left: -0.1rem;
        top: -0.1rem;
        right: -0.1rem;
        bottom: -0.1rem;
        border: 0.2rem outset grey;
        background-color: darkgrey;
    }

    #flag-mode {
        display: none;
    }

    [for="flag-mode"],
    [for="show-bombs"] {
        display: block;
        margin: 0 auto;
        width: fit-content;
        background: lightgrey;
        padding: 0 0.5rem;
        border: 2px outset grey;
        border-radius: 0.5rem;
        margin: 0.2rem auto;
    }

    #flag-mode:not(:checked) + [for="flag-mode"]:after {
        content: " OFF ";
        font-weight: bold;
    }

    #flag-mode:checked + [for="flag-mode"]:after {
        content: " ON ";
        font-weight: bold;
    }

    .open-square, .flag-square {
        position: absolute;
        left: -1000rem;
    }

    .open-square:not(.bomb) {
        counter-increment: total;
    }

    .open-square.bomb {
        counter-increment: bombs;
    }

    .open-square:checked + .square > label {
        display: none;
    }

    :not(.bomb)[data-count] + .square:before {
        line-height: 2rem;
        font-weight: bold;
    }

    :not(.bomb)[data-count="1"] + .square:before {
        content: "1";
        color: blue;
    }

    :not(.bomb)[data-count="2"] + .square:before {
        content: "2";
        color: green;
    }

    :not(.bomb)[data-count="3"] + .square:before {
        content: "3";
        color: red;
    }

    :not(.bomb)[data-count="4"] + .square:before {
        content: "4";
        color: darkblue;
    }

    :not(.bomb)[data-count="5"] + .square:before {
        content: "5";
        color: brown;
    }

    :not(.bomb)[data-count="6"] + .square:before {
        content: "6";
        color: cyan;
    }

    :not(.bomb)[data-count="7"] + .square:before {
        content: "7";
        color: black;
    }

    :not(.bomb)[data-count="8"] + .square:before {
        content: "8";
        color: grey;
    }

    .bomb + .square {
        background-color: red
    }

    .bomb + .square:before {
        content: "💣";
        line-height: 2rem;
        font-size: 1.2rem;
    }

    .flag {
        display: none;
    }

    .flag-square:checked ~ .flag {
        display: block;
        counter-increment: flagged;
    }

    .flag-square:checked ~ .flag-label:before,
    .flag-square:checked ~ .flag:before {
        content: "🚩"
    }

    #flag-mode:not(:checked) ~ .square > .flag-label {
        display: none;
    }

    #flag-mode:checked ~ .show-square:not(:checked) + .square > .flag-label {
        display: block;
    }

    .open-square:not(.bomb):checked {
        counter-increment: total cleared;
    }

    .bombs {
        width: 50%;
        text-align: center;
    }

    .score {
        clear: both;
        float: right;
        text-align: right;
        width: 50%;
        text-align: center;
    }

    .bombs:after {
        content: counter(bombs) "🚩" counter(flagged);
        line-height: 3rem;
        border: 0.2rem inset lightgrey;
        padding: 0.2rem 0.5rem;
        background-color: white;
        border-radius: 0.5rem;
    }

    .score:after {
        content: counter(cleared) " of " counter(total);
        line-height: 3rem;
        border: 0.2rem inset lightgrey;
        padding: 0.2rem 0.5rem;
        background-color: white;
        border-radius: 0.5rem;
    }

    .message {
        clear: left;
        color: limegreen;
        text-align: center;
        font-weight: bold;
    }

    .message:before {
        content: "(: YOU WIN! :) "
    }

    .pane {
        display: block;
        position: absolute;
        left: 0;
        right: 0;
        top: 0;
        bottom: 0;
    }

    .open-square:not(.grouped):not(.bomb):not(:checked) ~ .message:before,
    .group-master:not(:checked) ~ .message:before {
        content: none;
    }

    .open-square:not(.grouped):not(.bomb):not(:checked) ~ .pane,
    .group-master:not(:checked) ~ .pane,
    .lost-game {
        display: none;
    }

    .open-square.bomb:checked ~ .message {
        color: red;
    }

    .open-square.bomb:checked ~ .pane {
        position: relative;
        display: block;
    }

    .open-square.bomb:checked ~ .message:before {
        content: " ): You Lose :(" !important;
    }

    .open-square.bomb:checked ~ .lost-game {
        position: relative;
        display: block;
        z-index: 2
    }

    #show-bombs:checked ~ .bomb + .square > label {
        display: none;
    }

    #show-bombs:checked ~ .open-square:not(.bomb) + .square > .flag:after {
        content: "❌";
        position: absolute;
        top: -0.4rem;
        left: -0.1rem;
    }
    </style>
</head>
<body>
<div id="game">
    <h1>Bomb CSSweeper</h1>
    <input type="checkbox" id="show-bombs" hidden>
    <input type="checkbox" id="flag-mode">
    <label for="flag-mode">🚩mode</label>
    <div class="pane"></div>
    <div class="score">Cleared:
        <br>
    </div>
    <div class="bombs">Bombs:
        <br>
    </div>
    <div class="message"></div>
    <div class="lost-game">
        <label for="show-bombs">Show Bombs</label>
    </div>
</div>
<script>

    const rows = Math.floor(10 + (Math.random() * 5));
    const cols = Math.floor(10 + (Math.random() * 5));
    const bombs = Math.floor(15 + (Math.random() * 10));
    let i, s, l, fi, fs, fl;
    const game = document.getElementById('game');
    const pane = game.querySelector('.pane');
    const grid = [];
    const style = document.createElement('style');
    style.innerHTML = ".square:nth-of-type(" + cols + "n+1) {clear:left;}\n";
    game.insertBefore(style, pane);
    for (var r = 1; r <= rows; r++) {
        grid[r] = [];
        for (var c = 1; c <= cols; c++) {
            i = grid[r][c] = document.createElement('input');
            i.type = 'checkbox';
            i.classList.add('open-square')
            i.id = 'open-r' + r + '-c' + c;
            i.r = r;
            i.c = c;
            game.insertBefore(i, pane);
            s = document.createElement('span');
            s.classList.add('square');
            game.insertBefore(s, pane);
            l = document.createElement('label');
            l.htmlFor = i.id;
            l.classList.add('open-label');
            s.appendChild(l);
            fi = document.createElement('input');
            fi.type = "checkbox";
            fi.classList.add('flag-square');
            fi.id = 'flag-r' + r + '-c' + c;
            s.appendChild(fi);

            fs = document.createElement('span');
            fs.classList.add('flag');
            s.appendChild(fs);

            fl = document.createElement('label');
            fl.htmlFor = fi.id;
            fl.classList.add('flag-label');
            s.appendChild(fl);
        }
    }
    for (var b = 0; b < bombs; b++) {
        do {
            r = 1 + (Math.random() * rows) >> 0;
            c = 1 + (Math.random() * cols) >> 0;
            console.log(r, c, grid[r][c]);
        } while (grid[r][c].classList.contains('bomb'));
        i = grid[r][c];
        i.classList.add('bomb');

        var list = getSurrounding(i)
        for (r = 0; r < list.length; r++) {
            i = list[r];
            var count = Number(i.getAttribute('data-count')) || 0;
            i.setAttribute('data-count', ++count);
            i = i.nextElementSibling;
            i.setAttribute('data-count', count);
        }
    }
    var group = 1;
    while (i = document.querySelector('.open-square:not([data-count]):not(.grouped)')) {
        var check = [i];
        do {
            i = check.pop();
            list = getSurrounding(i)
            for (r = 0; r < list.length; r++) {
                i = list[r];
                if (!i.classList.contains('group' + group)) {
                    i.classList.add('grouped');
                    i.classList.add('group' + group);
                    if (!i.getAttribute('data-count')) {
                        check.push(i);
                        i = document.querySelector('[for="' + i.id + '"]');
                        i.htmlFor = 'group' + group;
                    }
                }
            }
        } while (check.length);
        i = document.createElement('input');
        i.type = 'checkbox';
        i.classList.add('group-master');
        i.hidden = true;
        i.id = 'group' + group;
        style.innerHTML += '#group' + group + ':checked ~ .group' + group + ' + .square > label { display:none; }\n' + '#group' + group + ':checked ~ .group' + group + ' { counter-increment:cleared total }\n';
        game.insertBefore(i, style);
        group++;
    }

    function getSurrounding(i) {
        var rs = Math.max(i.r - 1, 1);
        var re = Math.min(i.r + 1, rows);
        var cs = Math.max(i.c - 1, 1);
        var ce = Math.min(i.c + 1, cols);
        var result = [];
        for (r = rs; r <= re; r++) {
            for (c = cs; c <= ce; c++) {
                result.push(grid[r][c]);
            }
        }
        return result;
    }
</script>
</body>
</html>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈老说

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值