<svg id="main" class="hide">
<symbol id="bomb-svg" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 786.7 687.8" width="50px">
<path d="M427.4,144.2l40.7-41.8a23.1,23.1,0,0,1,32.5,0l72.8,72.8a23.1,23.1,0,0,1,0,32.5l-37.4,37.4" transform="translate(0 -11.6)" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17"/>
<circle cx="291.1" cy="385.2" r="282.6" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17"/>
<path d="M540.2,141.2l15.4-20.5c12.6-16.8,30.4-17.7,43.6-2.4l0.3,0.3c11.8,13.7,31.3,22.6,48.3,11.4,6.1-4,20.7-20.5,20.7-20.5" transform="translate(0 -11.6)" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17"/>
<line x1="701.7" y1="63.3" x2="742" y2="23.1" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17"/>
<line x1="713.2" y1="107.4" x2="766.2" y2="128.2" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17"/>
<line x1="654.5" y1="60.2" x2="630.8" y2="8.5" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17"/>
<path d="M82,396.8c0-118.4,95.9-214.3,214.3-214.3" transform="translate(0 -11.6)" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17"/>
<g id="Happy_face" data-name="Happy face">
<path d="M170.4,432.1a34.6,34.6,0,0,1,69.2,0" transform="translate(0 -11.6)" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17"/>
<path d="M342.5,432.1a34.6,34.6,0,0,1,69.2,0" transform="translate(0 -11.6)" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17"/>
<path d="M367,481.7c0,33.7-33.4,64.1-74.6,64.1s-74.6-30.3-74.6-64.1" transform="translate(0 -11.6)" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17"/>
</g>
</symbol>
</svg>
<div id="background"></div>
<div id="modal">
<div id="result-box">
<div id="result-top">
<svg id="bomb" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 774.7 676.2">
<path d="M427.4,144.2l40.7-41.8a23.1,23.1,0,0,1,32.5,0l72.8,72.8a23.1,23.1,0,0,1,0,32.5l-37.4,37.4" transform="translate(0 -11.6)" fill="#8b6af5" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17" class="bomb-stroke bomb-fill"/>
<circle cx="291.1" cy="385.2" r="282.6" fill="#8b6af5" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17" class="bomb-stroke bomb-fill"/>
<path d="M540.2,141.2l15.4-20.5c12.6-16.8,30.4-17.7,43.6-2.4l0.3,0.3c11.8,13.7,31.3,22.6,48.3,11.4,6.1-4,20.7-20.5,20.7-20.5" transform="translate(0 -11.6)" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17" class="bomb-stroke"/>
<line x1="701.7" y1="63.3" x2="742" y2="23.1" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17" class="bomb-stroke"/>
<line x1="713.2" y1="107.4" x2="766.2" y2="128.2" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17" class="bomb-stroke"/>
<line x1="654.5" y1="60.2" x2="630.8" y2="8.5" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17" class="bomb-stroke"/>
<path d="M82,396.8c0-118.4,95.9-214.3,214.3-214.3" transform="translate(0 -11.6)" fill="none" stroke="#fff" stroke-linecap="round" stroke-miterlimit="10" stroke-opacity="0.45" stroke-width="17"/>
<g id="happy-face" class="happy-face">
<path d="M170.4,432.1a34.6,34.6,0,0,1,69.2,0" transform="translate(0 -11.6)" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17" class="bomb-stroke"/>
<path d="M342.5,432.1a34.6,34.6,0,0,1,69.2,0" transform="translate(0 -11.6)" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17" class="bomb-stroke"/>
<path d="M367,481.7c0,33.7-33.4,64.1-74.6,64.1s-74.6-30.3-74.6-64.1" transform="translate(0 -11.6)" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17" class="bomb-stroke"/>
</g>
<g id="sad-face" class="sad-face">
<g>
<circle cx="377.1" cy="406" r="17.7" fill="#39395b" class="bomb-fill-dark"/>
<path d="M250,517.1c0-19.2,17.6-42.5,41.1-42.5s43.8,23.3,43.8,42.5" transform="translate(0 -11.6)" fill="none" stroke="#39395b" stroke-linecap="round" stroke-miterlimit="10" stroke-width="17" class="bomb-stroke"/>
<circle cx="205" cy="406" r="17.7" fill="#39395b" class="bomb-fill-dark"/>
</g>
</g>
</svg>
<h1 id="result-message"></h1>
<h2 class="result-time">Your time: <span class="time-display"></span> seconds</h2>
</div>
<div id="new-game">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 512 512" enable-background="new 0 0 512 512" fill="#fff" width="30">
<g>
<path d="M480.6,235.6c-11.3,0-20.4,9.1-20.4,20.4c0,112.6-91.6,204.2-204.2,204.2c-112.6,0-204.2-91.6-204.2-204.2 S143.4,51.8,256,51.8c61.5,0,118.5,27.1,157.1,73.7h-70.5c-11.3,0-20.4,9.1-20.4,20.4s9.1,20.4,20.4,20.4h114.6 c11.3,0,20.4-9.1,20.4-20.4V31.4c0-11.3-9.1-20.4-20.4-20.4s-20.4,9.1-20.4,20.4v59C390.7,40.1,325.8,11,256,11 C120.9,11,11,120.9,11,256c0,135.1,109.9,245,245,245s245-109.9,245-245C501,244.7,491.9,235.6,480.6,235.6z"/>
</g>
</svg>
<h2>重新开始</h2>
</div>
</div>
</div>
<div class="container">
<div class="header">
<div class='dropdown'>
<div class='title'>初级</div>
<div class='menu'>
<div class='option' id='option1'>初级</div>
<div class='option' id='option2'>中级</div>
<div class='option' id='option3'>高级</div>
</div>
</div>
<div id='flag-countdown'><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 287.987 287.987" fill="#fff" width="30" style="enable-background:new 0 0 287.987 287.987;" xml:space="preserve"><g><path d="M228.702,141.029c-3.114-3.754-3.114-9.193,0-12.946l33.58-40.474c2.509-3.024,3.044-7.226,1.374-10.783 c-1.671-3.557-5.246-5.828-9.176-5.828h-57.647v60.98c0,16.618-13.52,30.138-30.138,30.138h-47.093v25.86 c0,5.599,4.539,10.138,10.138,10.138h124.74c3.93,0,7.505-2.271,9.176-5.828c1.671-3.557,1.135-7.759-1.374-10.783L228.702,141.029 z"/><path d="M176.832,131.978V25.138c0-5.599-4.539-10.138-10.138-10.138H53.37c0-8.284-6.716-15-15-15s-15,6.716-15,15 c0,7.827,0,253.91,0,257.987c0,8.284,6.716,15,15,15s15-6.716,15-15c0-6.943,0-126.106,0-130.871h113.324 C172.293,142.116,176.832,137.577,176.832,131.978z"/></g></svg><span id='flags-left'></span></div>
<div id="timer"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 559.98 559.98" fill="#fff" width="30px" style="enable-background:new 0 0 559.98 559.98;" xml:space="preserve"><g><path d="M279.99,0C125.601,0,0,125.601,0,279.99c0,154.39,125.601,279.99,279.99,279.99c154.39,0,279.99-125.601,279.99-279.99 C559.98,125.601,434.38,0,279.99,0z M279.99,498.78c-120.644,0-218.79-98.146-218.79-218.79 c0-120.638,98.146-218.79,218.79-218.79s218.79,98.152,218.79,218.79C498.78,400.634,400.634,498.78,279.99,498.78z"/><path d="M304.226,280.326V162.976c0-13.103-10.618-23.721-23.716-23.721c-13.102,0-23.721,10.618-23.721,23.721v124.928 c0,0.373,0.092,0.723,0.11,1.096c-0.312,6.45,1.91,12.999,6.836,17.926l88.343,88.336c9.266,9.266,24.284,9.266,33.543,0 c9.26-9.266,9.266-24.284,0-33.544L304.226,280.326z"/></g></svg><span class="counter">000</span></div>
</div>
<div class="grid"></div>
</div>
HTML
格式化
支持Emmet,输入 p 后按 Tab键试试吧!
<head> ... </head>
<body>
</body>
$main: var(--main-color);
$dark: var(--dark-color);
$tile: #f3f1ff;
:root {
--grid-width: 350;
--tile-width: 35;
--main-color: #8B6AF5;
}
body {
padding: 0;
margin: 0;
background: #f9f8fe;
width: 100%;
height:100vh;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
font-family: 'Roboto Mono', monospace;
}
*,
*:before,
*:after {
box-sizing: border-box;
}
.container {
width: calc(var(--grid-width) * 1px);
align-content: center;
text-align: center;
box-shadow: 5px 5px 20px 0 rgba(100, 74, 74, 0.1);
position: relative;
z-index:2;
}
.header {
display: flex;
position: relative;
align-items: flex-start;
background-color: $main;
color: white;
justify-content: space-between;
padding:1rem;
}
.grid {
height: calc(var(--grid-width) * 1px);
width: 100%;
display: flex;
flex-wrap: wrap;
}
.tile {
height: calc(var(--tile-width) * 1px);
width: calc(var(--tile-width) * 1px);
cursor: pointer;
border: 2px solid;
border-color: lighten($tile, 5%) darken($tile, 5%) darken($tile, 5%) lighten($tile, 5%);
box-sizing: border-box;
background-color: $tile;
font-weight: 700;
font-size: 25px;
display: flex;
align-items: center;
justify-content: center;
}
.tile svg{
width: 80%;
}
.checked {
border: 1px solid;
background-color: darken($tile, 2%);
border-color: darken($tile, 5%);
}
#refresh {
cursor:pointer;
width:30px;
align-self: flex-end;
}
.dropdown {
color: white;
background-color: rgba(255, 255, 255, 0.2);
border-radius: 3px;
font-family: Verdana, Geneva, Tahoma, sans-serif;
font-size: 1rem;
text-align: center;
.title {
width: 100%;
padding:0.5rem 1rem;
cursor: pointer;
}
.menu {
background:$main;
position:absolute;
overflow: hidden;
cursor: pointer;
display: none;
width: 5rem;
text-align: left;
line-height:1.4rem;
&.show {
display:block;
}
.option{
padding: .5rem;
&:hover {
background: rgba(255, 255, 255, 0.2);
}
}
}
}
.has-bomb {
transition: background .25s ease-in;
}
#flag-countdown, #timer {
display: flex;
font-size:35px;
span {
margin-left:0.5rem;
}
}
#modal {
position: fixed;
background-color: rgba(#39395b, 0.2);
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 999;
visibility: hidden;
opacity: 0;
pointer-events: none;
transition: all 0.3s;
display: flex;
align-items: center;
justify-content: center;
&.show {
visibility: visible;
opacity: 1;
pointer-events: auto;
}
.modal-close {
text-align: right;
}
h2 {
color: $main;
}
}
#result-box {
background-color: #f9f8fe;
box-shadow: 5px 5px 20px 0 rgba(100, 74, 74, 0.1);
border-radius: 4px;
min-width: 400px;
text-align: center;
}
#result-top {
margin:2rem;
}
#result-message {
color: $dark;
font-size:40px;
}
.result-time {
display: none;
}
.show {
display: block;
}
#new-game {
padding:0.5rem;
background-color: $main;
color: #fff;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
margin:1rem;
height:60px;
border-radius: 4px;
font-family: sans-serif;
* {
display: inline-block;
}
h2 {
line-height:30px;
margin: 0 0 0 1rem;
color: white;
}
}
#background {
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1;
position: absolute;
z-index: 1;
width: 100%;
height: 100%;
svg {
width: 50px;
height: 40px;
opacity: 0.3;
position: absolute;
}
}
svg#bomb{
width: 200px;
.sad-face, .happy-face {
display: none;
&.show {
display: block;
}
}
.bomb-fill {
fill: $main;
}
.bomb-stroke {
stroke: $dark;
}
.bomb-fill-dark {
fill: $dark;
}
}
svg.hide {
display: none;
}
.shake {
animation: shake 0.75s cubic-bezier(.38,.06,.22,.95) both;
transform: translate3d(0, 0, 0);
backface-visibility: hidden;
perspective: 1000px;
}
@keyframes shake {
10%, 90% {
transform: translate3d(-1px, 0, 0);
}
20%, 80% {
transform: translate3d(2px, 0, 0);
}
30%, 50%, 70% {
transform: translate3d(-3px, 0, 0);
}
40%, 60% {
transform: translate3d(2px, 0, 0);
}
}
// watch Ania Kubow's tutorial here: https://www.youtube.com/watch?v=rxdGAKRndz8
document.addEventListener("DOMContentLoaded", () => {
const grid = document.querySelector(".grid");
const container = document.querySelector(".container");
const flagsLeft = document.querySelector("#flags-left");
const result = document.querySelector("#result-message");
const modal = document.querySelector("#modal");
const background = document.querySelector("#background");
const bombSadFace = document.querySelector("#sad-face");
const bombHappyFace = document.querySelector("#happy-face");
const timer = document.querySelector(".counter");
const finalTimeDisplay = document.querySelector(".time-display");
const resultTime = document.querySelector(".result-time");
const levels = [
{
name: "初级",
width: 10,
bombs: 15,
},
{
name: "中级",
width: 15,
bombs: 30,
},
{
name: "高级",
width: 20,
bombs: 70,
},
];
const flagIcon =
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 287.987 287.987" fill="#695ca8" style="enable-background:new 0 0 287.987 287.987;" xml:space="preserve"><g><path d="M228.702,141.029c-3.114-3.754-3.114-9.193,0-12.946l33.58-40.474c2.509-3.024,3.044-7.226,1.374-10.783 c-1.671-3.557-5.246-5.828-9.176-5.828h-57.647v60.98c0,16.618-13.52,30.138-30.138,30.138h-47.093v25.86 c0,5.599,4.539,10.138,10.138,10.138h124.74c3.93,0,7.505-2.271,9.176-5.828c1.671-3.557,1.135-7.759-1.374-10.783L228.702,141.029 z"/><path d="M176.832,131.978V25.138c0-5.599-4.539-10.138-10.138-10.138H53.37c0-8.284-6.716-15-15-15s-15,6.716-15,15 c0,7.827,0,253.91,0,257.987c0,8.284,6.716,15,15,15s15-6.716,15-15c0-6.943,0-126.106,0-130.871h113.324 C172.293,142.116,176.832,137.577,176.832,131.978z"/></g></svg>';
const bombIcon =
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 512 512" fill="#695ca8" ><g><path d="m411.313,123.313c6.25-6.25 6.25-16.375 0-22.625s-16.375-6.25-22.625,0l-32,32-9.375,9.375-20.688-20.688c-12.484-12.5-32.766-12.5-45.25,0l-16,16c-1.261,1.261-2.304,2.648-3.31,4.051-21.739-8.561-45.324-13.426-70.065-13.426-105.867,0-192,86.133-192,192s86.133,192 192,192 192-86.133 192-192c0-24.741-4.864-48.327-13.426-70.065 1.402-1.007 2.79-2.049 4.051-3.31l16-16c12.5-12.492 12.5-32.758 0-45.25l-20.688-20.688 9.375-9.375 32.001-31.999zm-219.313,100.687c-52.938,0-96,43.063-96,96 0,8.836-7.164,16-16,16s-16-7.164-16-16c0-70.578 57.422-128 128-128 8.836,0 16,7.164 16,16s-7.164,16-16,16z"/><path d="m459.02,148.98c-6.25-6.25-16.375-6.25-22.625,0s-6.25,16.375 0,22.625l16,16c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688 6.25-6.25 6.25-16.375 0-22.625l-16.001-16z"/><path d="m340.395,75.605c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688 6.25-6.25 6.25-16.375 0-22.625l-16-16c-6.25-6.25-16.375-6.25-22.625,0s-6.25,16.375 0,22.625l15.999,16z"/><path d="m400,64c8.844,0 16-7.164 16-16v-32c0-8.836-7.156-16-16-16-8.844,0-16,7.164-16,16v32c0,8.836 7.156,16 16,16z"/><path d="m496,96.586h-32c-8.844,0-16,7.164-16,16 0,8.836 7.156,16 16,16h32c8.844,0 16-7.164 16-16 0-8.836-7.156-16-16-16z"/><path d="m436.98,75.605c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688l32-32c6.25-6.25 6.25-16.375 0-22.625s-16.375-6.25-22.625,0l-32,32c-6.251,6.25-6.251,16.375-0.001,22.625z"/></g></svg>';
let root = document.documentElement;
let selectedLevel = levels[0];
let flags = 0;
let tiles = [];
let isGameOver = false;
let isTimerOn = false;
let timerCount = 0;
let finalTime = 0;
let numberColors = [
"#8B6AF5",
"#74c2f9",
"#42dfbc",
"#f9dd5b",
"#FEAC5E",
"#ff5d9e",
"#F29FF5",
"#c154d8",
];
let bgColors = [
"#b39ffd",
"#93c1fd",
"#8af1f8",
"#f9dd5b",
"#FEAC5E",
"#f87dae",
"#f6b8f8",
"#f7efce",
];
function getMainColor() {
const randomColor =
numberColors[Math.floor(Math.random() * numberColors.length)];
root.style.setProperty("--main-color", randomColor);
root.style.setProperty(
"--dark-color",
lightenDarkenColor(randomColor, -50)
);
}
function clearBoard() {
isGameOver = false;
flags = 0;
if (isTimerOn) stopTimer();
timerCount = 0;
// isTimerOn = false;
timer.innerHTML = "000";
tiles.forEach((tile) => {
tile.remove();
});
tiles = [];
container.classList.remove("shake");
bombHappyFace.classList.remove("show");
bombSadFace.classList.remove("show");
resultTime.classList.remove("show");
createBoard();
}
function setUp() {
createBackground();
createBoard();
}
function createBoard() {
getMainColor();
flagsLeft.innerHTML = selectedLevel.bombs;
const width = selectedLevel.width;
const tileWidth = parseInt(
getComputedStyle(root).getPropertyValue("--tile-width")
);
root.style.setProperty("--grid-width", width * tileWidth);
//add tiles
for (let i = 0; i < width * width; i++) {
const tile = document.createElement("div");
tile.setAttribute("id", i);
tile.classList.add("tile");
grid.appendChild(tile);
tiles.push(tile);
//left click
tile.addEventListener("click", () => clickTile(tile));
//ctrl and right click
tile.oncontextmenu = (e) => {
e.preventDefault();
addFlag(tile);
};
}
//add bombs
const randomTiles = tiles
.sort(() => Math.random() - 0.5)
.slice(0, selectedLevel.bombs)
.map((tile) => tile.id); // suffle tile's id and select 20 firsts to get 20 random tiles
tiles.forEach((tile) =>
randomTiles.includes(tile.id)
? tile.classList.add("has-bomb")
: tile.classList.add("is-empty")
); // if tile's id is in ramdomTile array, add bomb
tiles.sort((a, b) => a.id - b.id); //sort array by id again
//add numbers
for (let i = 0; i < tiles.length; i++) {
let total = 0;
const isLeftEdge = i % width === 0;
const isRightEdge = i % width === width - 1;
if (!tiles[i].classList.contains("has-bomb")) {
if (!isLeftEdge) {
if (tiles[i - 1] && tiles[i - 1].classList.contains("has-bomb"))
total++;
if (
tiles[i - 1 + width] &&
tiles[i - 1 + width].classList.contains("has-bomb")
)
total++;
if (
tiles[i - 1 - width] &&
tiles[i - 1 - width].classList.contains("has-bomb")
)
total++;
}
if (!isRightEdge) {
if (tiles[i + 1] && tiles[i + 1].classList.contains("has-bomb"))
total++;
if (
tiles[i + 1 + width] &&
tiles[i + 1 + width].classList.contains("has-bomb")
)
total++;
if (
tiles[i + 1 - width] &&
tiles[i + 1 - width].classList.contains("has-bomb")
)
total++;
}
if (tiles[i - width] && tiles[i - width].classList.contains("has-bomb"))
total++;
if (tiles[i + width] && tiles[i + width].classList.contains("has-bomb"))
total++;
tiles[i].setAttribute("data", total);
}
}
}
//click on tile
function clickTile(tile) {
if (!isTimerOn) startTimer();
let currentId = tile.id;
if (isGameOver) return;
if (tile.classList.contains("checked") || tile.classList.contains("flag"))
return;
if (tile.classList.contains("has-bomb")) {
gameOver(tile);
} else {
let total = tile.getAttribute("data");
if (total != 0) {
tile.classList.add("checked");
tile.style.color = numberColors[total - 1];
tile.style.textShadow =
"1px 1px" + lightenDarkenColor(numberColors[total - 1], -20);
tile.innerHTML = total;
return;
}
checktile(currentId);
}
tile.classList.add("checked");
}
//check neighboring tiles once tile is clicked
function checktile(currentId) {
const width = selectedLevel.width;
const isLeftEdge = currentId % width === 0;
const isRightEdge = currentId % width === width - 1;
const parsedId = parseInt(currentId);
function loopThroughtiles(tileId) {
const newId = tileId.id;
const newTile = document.getElementById(newId);
clickTile(newTile);
}
setTimeout(() => {
if (!isRightEdge) {
if (tiles[parsedId + 1 - width])
loopThroughtiles(tiles[parsedId + 1 - width]);
if (tiles[parsedId + 1]) loopThroughtiles(tiles[parsedId + 1]);
if (tiles[parsedId + 1 + width])
loopThroughtiles(tiles[parsedId + 1 + width]);
}
if (!isLeftEdge) {
if (tiles[parsedId - 1]) loopThroughtiles(tiles[parsedId - 1]);
if (tiles[parsedId - 1 - width])
loopThroughtiles(tiles[parsedId - 1 - width]);
if (tiles[parsedId - 1 + width])
loopThroughtiles(tiles[parsedId - 1 + width]);
}
if (tiles[parsedId - width]) loopThroughtiles(tiles[parsedId - width]);
if (tiles[parsedId + width]) loopThroughtiles(tiles[parsedId + width]);
}, 50);
}
//game over
function gameOver(currentTile) {
isGameOver = true;
stopTimer();
currentTile.innerHTML = bombIcon;
container.classList.add("shake");
currentTile.style.backgroundColor =
bgColors[Math.floor(Math.random() * bgColors.length)];
currentTile.classList.remove("has-bomb");
currentTile.classList.add("checked");
let itemsProcessed = 0;
//show all the bombs
const bombTiles = tiles.filter((tile) =>
tile.classList.contains("has-bomb")
);
bombTiles.forEach((tile, index) => {
setTimeout(() => {
tile.innerHTML = bombIcon;
tile.style.backgroundColor =
bgColors[Math.floor(Math.random() * bgColors.length)];
tile.classList.remove("has-bomb");
tile.classList.add("checked");
itemsProcessed++;
if (itemsProcessed === bombTiles.length) {
setTimeout(() => {
modal.classList.add("show");
bombSadFace.classList.add("show");
result.innerHTML = "游戏结束!";
}, 1000);
}
}, 10 * index);
});
}
//add Flag with right click
function addFlag(tile) {
if (isGameOver) return;
if (!tile.classList.contains("checked") && flags < selectedLevel.bombs) {
if (!tile.classList.contains("flag")) {
tile.classList.add("flag");
tile.innerHTML = flagIcon;
flags++;
flagsLeft.innerHTML = selectedLevel.bombs - flags;
checkForWin();
} else {
tile.classList.remove("flag");
tile.innerHTML = "";
flags--;
flagsLeft.innerHTML = selectedLevel.bombs - flags;
}
}
}
//check for win
function checkForWin() {
let matches = 0;
tiles.forEach((tile) => {
if (
tile.classList.contains("flag") &&
tile.classList.contains("has-bomb")
) {
matches++;
}
if (matches === selectedLevel.bombs) {
stopTimer();
modal.classList.add("show");
bombHappyFace.classList.add("show");
resultTime.classList.add("show");
result.innerHTML = "恭喜!";
isGameOver = true;
// reveal all remaining tiles
if (!tile.classList.contains("checked")) {
tile.classList.add("checked");
}
}
});
}
function replay() {
if (modal.classList.contains("show")) modal.classList.remove("show");
clearBoard();
}
document.querySelector("#new-game").addEventListener("click", replay);
// timer functions
function startTimer() {
isTimerOn = true;
let sec = 0;
timerCount = setInterval(() => {
sec++;
timer.innerHTML = ("00" + sec).slice(-3);
if (sec > 998) clearInterval(timerCount);
finalTime = sec;
}, 1000);
}
function stopTimer() {
clearInterval(timerCount);
isTimerOn = false;
finalTimeDisplay.innerHTML = finalTime;
}
// dropdown menu functions
const dropdownTitle = document.querySelector(".dropdown .title");
const dropdownOptions = document.querySelectorAll(".dropdown .option");
function toggleMenuDisplay(e) {
const dropdown = e.target.parentNode; //getting the parent selector
const menu = dropdown.querySelector(".menu"); //selecting 'menu' fron the parent selector
menu.classList.toggle("show");
}
function handleOptionSelected(e) {
e.target.parentNode.classList.toggle("show"); // using parentnode to get the menu elementfrom option elements
dropdownTitle.textContent = e.target.textContent;
// here: select current level form levels object
selectedLevel = levels.find((level) => level.name === e.target.textContent);
clearBoard();
}
// event listeners
dropdownTitle.addEventListener("click", toggleMenuDisplay);
dropdownOptions.forEach((option) =>
option.addEventListener("click", handleOptionSelected)
);
// bg functions
function addElement(x, y) {
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
const use = document.createElementNS("http://www.w3.org/2000/svg", "use");
use.setAttributeNS(
"http://www.w3.org/1999/xlink",
"xlink:href",
"#bomb-svg"
);
svg.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
svg.setAttribute("style", "top: " + y + "px; left: " + x + "px");
svg.appendChild(use);
background.appendChild(svg);
}
function createBackground() {
const spacing = 60;
const w = window.innerWidth;
const h = window.innerHeight;
for (let y = 0; y <= h; y += spacing) {
if (y % (spacing * 2) === 0) {
for (let x = 0; x <= w; x += spacing) {
addElement(x, y);
}
} else {
for (let x = -(spacing / 2); x <= w; x += spacing) {
addElement(x, y);
}
}
}
}
//helper function
function lightenDarkenColor(col, amt) {
let usePound = false;
if (col[0] == "#") {
col = col.slice(1);
usePound = true;
}
let num = parseInt(col, 16);
let r = (num >> 16) + amt;
if (r > 255) r = 255;
else if (r < 0) r = 0;
let b = ((num >> 8) & 0x00ff) + amt;
if (b > 255) b = 255;
else if (b < 0) b = 0;
let g = (num & 0x0000ff) + amt;
if (g > 255) g = 255;
else if (g < 0) g = 0;
return (usePound ? "#" : "") + (g | (b << 8) | (r << 16)).toString(16);
}
setUp();
});