用原生JS寫一個網頁版的2048小游戲(相容移動端)

来源:https://www.cnblogs.com/yuanyiming/archive/2019/03/09/10480959.html
-Advertisement-
Play Games

這個游戲JS部分全都是用原生JS代碼寫的,加有少量的CSS3動畫,並簡單的相容了一下移動端。 ...


 這個游戲JS部分全都是用原生JS代碼寫的,加有少量的CSS3動畫,並簡單的相容了一下移動端。

先看一下線上的demo:https://yuan-yiming.github.io/2048-online-game/

github地址:https://github.com/Yuan-Yiming/2048-online-game

 

 ====================================================================

 

下麵簡單分析一下JS代碼:

1.游戲中包含的主要事件:

new game按鈕的click事件:點擊後重新開始一次新的游戲;

game over後重新開始按鈕的click事件:點擊後重新開始一次新的游戲;

桌面端鍵盤上的方向鍵keydown的事件:按下方向鍵後數字會向相應的方向移動;

移動端的touch事件:通過計算touchstart和touchend的坐標變化,判斷手指在移動端屏幕的滑動方向;

 

2.業務邏輯

2.1 重新開始一次游戲:

需要清空游戲盤,並且隨機給一個格子生成一個數字2,每次生成不同數字都會給格子配相應的背景顏色;

2.2 按下方向盤或滑動屏幕時格子移動情況(核心部分

游戲面板有4 x 4的格子,遍歷每一行的每一個格子,若為空格則不作處理,非空格則要判斷其下一步(註意每次只判斷一步的動作,方法howToGo())的運動情況,即:

a:空格,不作處理

b:非空格,進一步判斷下一步動作,有3種情況

  b1:若需要移動的格子前面是沒有數字的空格,則格子往前移動一個,即將數字插入到前面格子內,原本格子中的數字銷毀,然後從b開始迴圈再次判斷;

  b2:若需要移動的格子前面不是空格,且與前面格子包含的數字不相等,則格子無需移動,該格子運動判斷完畢,進行下一個格子的運動判斷;

  b3:若需要移動的格子前面不是空格,且與前面格子包含的數字相等,則將前面格子內的數字 x 2,原本格子中的數字銷毀,該格子運動判斷完畢,進行下一個格子的運動判斷;

註意:由於是每次運動都是由運動方向的最前面格子開始運動的,即b2,b3兩種情況碰到前面有格子時,說明前面的格子已經完成運動,前面沒有空位了,所以當前格子運動完成,繼續移動後面的格子。

 

以下使用圖片來說明格子運動的情況:

上面有兩行格子,如果是向左運動,首先遍歷第1行,對1~4號格子依次判斷其運動情況,首先1號格子先運動,由於是1號格子靠邊,可認為其前面是包含不同數字的格子,運動情況為b2,無需運動;2號格子為空格,運動情況為a;3號格子前面為空格,首先運動情況為b1,運動後3號格子的數字插到2號格子,3號格子數字銷毀成為空格,然後需要對2號格子再次判斷,再進行一次運動,即b3,使得1號格子數字變成4,2號格子成為空格;後面4號格子為空格不作處理。遍歷第2行,對5~8號格子依次判斷其運動情況,5號格子與1號格子情況相同,6、7、8號格子為空格,無需處理。

向左運動後的格子:

 

如果是向右運動,遍歷第1行要從右邊開始,4~1號格子依次運動,首先4號格子空格無需處理;然後3號格子運動情況b1,向前移動一格,數字2插入到4號格子內;2號空格無需處理;1號空格先連續進行兩次b1運動,再進行一次b3運動,碰到4號格子內數字相同,最終使得4號格子內數字變成4。遍歷第2行,8~5號格子依次運動,8、7、6號為空格均無需處理,5號格子連續3次b1運動,使得8號格子內數字變成2

向右運動後的格子:

 

向上或者向下運動同理。

 

=====================================================================

 

下麵廢話不說,直接上代碼。

HTML代碼:index.html

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
	<title>Play 2048 Game Online!</title>
	<link rel="stylesheet" type="text/css" href="desktop.css">
	<link media="(max-width: 860px)" rel="stylesheet" type="text/css" href="mobile.css">
	<link rel="icon" href="icon.png">
</head>
<body>
	<!-- <div class="github"><a href="https://www.baidu.com"></a></div> -->
	<div class="wrapper">

		<div class="header">
			<div class="title">
				<strong>2048</strong>
			</div>
			<div class="slogan">
				<p>Play 2048 Game Online!</p>
			</div>
			
			<div class="score">
				<span>Score</span><br><span class="number">0</span><span class="score-animation">+12</span>
			</div>
			<div class="best">
				<span>Best</span><br><span class="number">1024</span><span class="best-animation">+12</span>
			</div>
			<div class="new-game">
				<span>New Game</span>
			</div>
		</div>
		<div class="game-board">
			<div class="grid grid-11 row1 col1"><span></span></div>
			<div class="grid grid-12 row1 col2"><span></span></div>
			<div class="grid grid-13 row1 col3"><span></span></div>
			<div class="grid grid-14 row1 col4"><span></span></div>
			<div class="grid grid-21 row2 col1"><span></span></div>
			<div class="grid grid-22 row2 col2"><span></span></div>
			<div class="grid grid-23 row2 col3"><span></span></div>
			<div class="grid grid-24 row2 col4"><span></span></div>
			<div class="grid grid-31 row3 col1"><span></span></div>
			<div class="grid grid-32 row3 col2"><span></span></div>
			<div class="grid grid-33 row3 col3"><span></span></div>
			<div class="grid grid-34 row3 col4"><span></span></div>
			<div class="grid grid-41 row4 col1"><span></span></div>
			<div class="grid grid-42 row4 col2"><span></span></div>
			<div class="grid grid-43 row4 col3"><span></span></div>
			<div class="grid grid-44 row4 col4"><span></span></div>
		</div>
		<div class="popup">
			<div class="game-over">
				<p>Game is over...</p>
				<p class="try-again">Try again?</p>
			</div>
			<div class="win">
				<p>You win the game!! </p><p>Congratulation!!</p>
				<p class="try-again">Try again?</p>
			</div>
		</div>
	</div>
	<script type="text/javascript" src="2048.js"></script>
	
</body>
</html>

  

CSS桌面版代碼:desktop.css

* {
	list-style: none;
	color: #8f7a66;
	padding: 0;
	margin: 0;
	box-sizing: border-box;
}
body {
	background-color: #ffffe0;
}

.wrapper {
	position: relative;
	width: 400px;
	height: 540px;
	/*border: 1px solid red;*/
	margin: 0 auto;
}
/*頭部*/
.header {
	width: 400px;
	height: 140px;
	/*border: 1px solid green;*/
	position: relative;
	/*opacity: 0.4;*/
}
.title, .slogan, .score, .best, .new-game, .github>a {
	position: absolute;
}
.title strong {
	display: inline-block;
	width: 260px;
	height: 100px;
	font-size: 78px;
	line-height: 100px;
	/*text-align: center;*/
	padding: 0 5px;
	/*border: 1px solid black;*/
}
.slogan {
	padding: 0 5px;
	top: 85px;
	/*border: 1px solid black;*/
}
.github>a {
	display: inline-block;
	width: 50px;
	height: 50px;
	/*border: 1px solid red;*/
	top: 8%;
	right: 5%;
	/*margin: 5px;*/
	background: url("Git.png") no-repeat 0 0;
	border-radius: 50%;
	background-size: 100%;
}
.github>span>span {
	display: inline-block;
	width: 100px;
	height: 16px;
	line-height: 16px;
	position: absolute;
	bottom: -24px;
	left: -25px;
	text-align: center;
	color: #2c2c2c;
}


/*分數*/

/* 分數動畫 */
.score-animation, .best-animation{
	display: none;
	position: absolute;
	top: 25px;
	left: 10px;
	width: 65px;
	height: 30px;
	font-size: 24px;
	font-weight: bold;
}
.score {
	left: 220px;
}
.best {
	left: 315px;
}
.score, .best {
	position: absolute;
	width: 85px;
	height: 60px;
	line-height: 28px;
	top: 20px;
	background-color: #bbada0;
}
.score span, .best span, .new-game span {
	color: #ffffff;
}
.score, .best, .new-game, .game-board, .grid {
	text-align: center;
	border-radius: 5px;
} 
.best .number, .score .number, .new-game {
	font-size: 22px;
	font-weight: bold;
}
.new-game {
	width: 180px;
	height: 40px;
	line-height: 40px;
	left: 220px;
	top: 90px;
	text-align: center;
	background-color: #8e7963;
	cursor: pointer;
}
.new-game:hover {
	width: 182px;
	height: 42px;
	line-height: 42px;
	left: 219px;
	top: 89px;
	font-size: 24px;
}



/*游戲主面板*/
.game-board {
	width: 400px;
	height: 400px;
	padding: 5px 5px;
	background-color: #bbada0;
	/*opacity: 0.4;*/
}
.grid {
	position: relative;
	float: left;
	width: 87.5px;
	height: 87.5px;
	line-height: 87.5px;
	/*font-size: 48px;*/
	font-weight: bold;
	margin: 5px;
	background-color: #b0c4de;
}
.game-board .grid span {
	/*color: */
	
}
/*game over or win the game彈出頁面*/
.popup .game-over, .popup .win {
	position: absolute;
	left: 60px;
	text-align: center;
	width: 280px;
	height: 160px;
	border-radius: 5px;
	/*border: 1px solid red;*/
	opacity: 1.0;
}
.popup p {
	color: #8f7a66;
}
.popup .game-over {
	display: none;
	top: 230px;
	font-size: 36px;
	font-weight: bold;
}

.popup .win {
	display: none;
	top: 220px;
	font-size: 28px;
	font-weight: bold;
}

p.try-again {
	color: #fff;
	font-size: 22px;
	width: 150px;
	height: 50px;
	line-height: 50px;
	border-radius: 5px;
	margin: 0 auto;
	background-color: #8f7a66;
	cursor: pointer;
}
.header, .game-board {
	/*opacity: 0.4;*/
}

.new-grid {
	background-color: #b0c4de !important;
}

/* 生成新格子動畫 */
@keyframes tempgrid {
	from {width: 45px; height: 45px; top: 24px;left: 24px; font-size: 18px; line-height: 45px;display: block;}
	to {width: 87.5px; height: 87.5px; top: 0px;left: 0px; font-size: 48px; line-height: 87.5px;display: none;}
}
@-webkit-keyframes tempgrid {
	from {width: 45px; height: 45px; top: 24px;left: 24px; font-size: 18px; line-height: 45px;display: block;}
	to {width: 87.5px; height: 87.5px; top: 0px;left: 0px; font-size: 48px; line-height: 87.5px;display: none;}
}
@-moz-keyframes tempgrid {
	from {width: 45px; height: 45px; top: 24px;left: 24px; font-size: 18px; line-height: 45px;display: block;}
	to {width: 87.5px; height: 87.5px; top: 0px;left: 0px; font-size: 48px; line-height: 87.5px;display: none;}
}
.temp-grid {
	animation: tempgrid 0.15s ease-in forwards;
	-webkit-animation: tempgrid 0.15s ease-in forwards;
	-ms-animation: tempgrid 0.15s ease-out forwards;
	-moz-animation: tempgrid 0.15s ease-out forwards;
}

  

CSS移動端相容代碼:mobile.css

body {
	background-color: #fffadd;
}

.wrapper {
	width: 100%;
	/*height: 540px;*/
	/*border: 1px solid red;*/
	/*margin: 0 auto;*/
}

/*頭部*/
.header {
	width: 100%;
	/*height: 140px;*/
	position: relative;
}
.title, .slogan, .score, .best, .new-game {
	position: absolute;
	float: left;
	text-align: center;
}


.title, .slogan {
	width: 100%;
	
}

.title strong {
	display: inline-block;
	width: 100%;
	/*height: 260px;*/
	font-size: 487%;  /* 100% 16px */
	/*line-height: 192%;*/
	text-align: center;
	/*padding: 0 5px;*/
	/*border: 1px solid black;*/
}
.slogan {
	/*margin-top: 10px;*/
	top: 65%;
}

/* github */
/*.github>a {
	top: 4%;
	right: 6.25%;
}*/


/*分數*/
.score, .best, .new-game {
	width: 25%;
	/*border: 1px solid green;*/
}

/* 分數動畫 */
.score-animation, .best-animation{
	display: none;
	/*position: absolute;*/
	top: 25px;
	left: 10px;
	width: 65px;
	height: 30px;
	font-size: 24px;
	font-weight: bold;
}

.score, .best {
	/*position: absolute;*/
	line-height: 28px;
	top: 90%;
	background-color: #bbada0;
}
.score {
	left: 47.5%;
}
.best {
	left: 75%;
}
.new-game {
	width: 45%;
	height: 60px;
	line-height: 60px;
	left: 0;
	top: 90%;
	text-align: center;
	background-color: #8e7963;
	cursor: pointer;
	/*padding-bottom: 2em;*/
	font-size: 28px;
}
.new-game:hover {
	width: 45%;
	/*height: 42px;*/
	/*line-height: 42px;*/
	height: 60px;
	line-height: 60px;
	left: 0;
	top: 90%;
	/*line-height: 2e;*/
	font-size: 28px;
}

.score span, .best span, .new-game span {
	color: #ffffff;
}
.score, .best, .new-game, .game-board, .grid {
	text-align: center;
	border-radius: 5px;
} 
.best .number, .score .number {
	font-size: 22px;
	font-weight: bold;
}

/*游戲主面板*/
.game-board {
	position: absolute;
	/*display: none;*/
	width: 100%;
	height: auto;
	/*height: 400px;*/
	/*padding: 10px 10px;*/
	background-color: #bbada0;
	/*opacity: 0.4;*/
	top: 36%;
}
.grid {
	/*position: relative;*/
	float: left;
	width: 22%;
	/*height: */
	/*padding-bottom: 21.95%;*/
	/*height: 100%;*/
	line-height: 80px;
	/*font-size: 48px;*/
	font-weight: bold;
	/*padding-bottom: 410px;*/
	padding: 1.5%;
	background-color: #b0c4de;
}

/* 生成新格子動畫 */
@keyframes tempgrid {
	from {width: 50%; height: 50%; top: 25%;left: 25%; font-size: 24px; line-height: 192%;display: block;}
	to {width: 100%; height: 100%; top: 0px;left: 0px; font-size: 48px; line-height: 192%;display: none;}
}
@-webkit-keyframes tempgrid {
	from {width: 50%; height: 50%; top: 25%;left: 25%; font-size: 24px; line-height: 192%;display: block;}
	to {width: 100%; height: 100%; top: 0px;left: 0px; font-size: 48px; line-height: 192%;display: none;}
}
@-moz-keyframes tempgrid {
	from {width: 50%; height: 50%; top: 25%;left: 25%; font-size: 24px; line-height: 192%;display: block;}
	to {width: 100%; height: 100%; top: 0px;left: 0px; font-size: 48px; line-height: 192%;display: none;}
}

  

js代碼:2048.js

// 頁面載入時
window.onload = function () {
	// var temp = tempGrid(2);
	giveNumber(2);
	newGameBotton();
	getReady();
	backgroundColorToNumber();
	scaleWidth();
	touch();
}


// 在移動端使得格子寬高比例1:1
function scaleWidth() {
	// 獲取格子的寬度
	var grid = document.getElementsByClassName("grid"),
		width = window.getComputedStyle(grid[0], null)["width"];
		// width = grid[0].style.width;
	//給格子高度賦值
	for (var i = 0; i < 16; i++) {
		grid[i].style.height = width;
	}	
}

// 創建一個臨時的格子
function createTempGrid(num) {
		var temp = document.createElement("div");
		temp.innerHTML = "<span>" + num + "</span>";	
		temp.style.position = "absolute";
		temp.style.backgroundColor = "#fff8dc";
		temp.style.width = "87.5px";
		temp.style.height = "87.5px";
		temp.style.lineHeight = "87.5px";
		temp.style.fontWeight = "bold";
		temp.style.fontSize = "48px";
		temp.style.borderRadius = "5px";
		temp.style.top = "0";
		temp.style.left = "0";
		// temp.style.display = "none";
		// console.log(temp);
		temp.classList.add("temp-grid");
		return temp;
	};

// 刪除臨時格子
function deleteTempGrid() {
	var temp = document.getElementsByClassName("temp-grid");
	for (var i = 0; i < temp.length; i ++) {
		temp[i].remove();
	}
	var newGrid = document.getElementsByClassName("new-grid");
		// console.log(newGrid);
	if (newGrid) {
		// console.log(newGrid.length);
		for (var i = 0; i < newGrid.length; i ++) {
			newGrid[i].classList.remove("new-grid");
			// console.log(newGrid.length);
		}
	}
}

// giveNumber:隨機生成一個空格子,每個空格子裡面放一個數字num
function giveNumber(num) {
	// console.log("give!!!!");
	var x, y, newGrid, tempGrid;
	
	tempGrid = createTempGrid(num);
	
	while (true) {
		// if (tempGrid && tempGrid.parentElement) {
		// 	tempGrid.parentElement.removeChild(tempGrid);
		// }
		x = Math.floor(Math.random() * 4) + 1;
		y = Math.floor(Math.random() * 4) + 1;
		newGrid = document.getElementsByClassName("grid-" + x + y)[0];
		
		// newGrid.style.backgroundColor = "#b0c4de";
		if (newGrid.innerHTML == "<span></span>") {
			newGrid.classList.add("new-grid");
			newGrid.innerHTML = "<span>" + num + "</span>";
			newGrid.appendChild(tempGrid);
			break;
		}
	}
	// return blankGrid;
}

// clearGrid:清空格子及分數歸零
function clearGrid() {
	var grid = document.getElementsByClassName("grid"),
		score = document.getElementsByClassName("score")[0].children[2];
	score.innerText = "0";
	for (var i = 0; i < grid.length; i ++) {
		grid[i].innerHTML = "<span></span>";
		// grid[i].style.backgroundColor = "#b0c4de";
	}
	backgroundColorToNumber();
}

// 重新開始一次游戲
function newGame() {
	clearGrid();
	giveNumber(2);
	backgroundColorToNumber();
	return true;
}

// 觸發新一次游戲的按鈕
function newGameBotton() {
	var newGameBtn = document.getElementsByClassName("new-game")[0];
	newGameBtn.addEventListener("click", function () {
		newGame();	
	}, false); 
	newGameBtn.addEventListener("touchend", function () {
		newGame();
	}, false); 
}

// backgroundColorToNumber:數字跟背景顏色/數字大小對應
function backgroundColorToNumber() {
	var gridNum,
		// child,
		grid = document.getElementsByClassName("grid");
	for (var i = 0; i < grid.length; i ++) {
		gridNum = getGridNum(grid[i]);
		// child = grid[i].children[0];
		grid[i].style.fontSize = "48px";
		// if ((" " + grid[i].className + " ").indexOf(" " + "new-grid" + " ") == -1) {
			switch (gridNum) {
				case 2:
					grid[i].style.backgroundColor = "#fff8dc";
					// grid[i].children[0].style.color = "#fff";   // 這句代碼會使得頁面癱瘓!!
					break;
				case 4:
					grid[i].style.backgroundColor = "#e9967a";
					// grid[i].children[0].style.color = "#8f7a66";	
					break;
				case 8:
					grid[i].style.backgroundColor = "#FFA07A";
					break;
				case 16:
					grid[i].style.backgroundColor = "#F4A460";
					break;
				case 32:
					grid[i].style.backgroundColor = "#FA8072";
					break;
				case 64:
					grid[i].style.backgroundColor = "#ff7f50";
					break;
				case 128:
					grid[i].style.backgroundColor = "#FF6347";
					grid[i].style.fontSize = "40px";
					break;
				case 256:
					grid[i].style.backgroundColor = "#FF8800";
					grid[i].style.fontSize = "40px";
					break;
				case 512:
					grid[i].style.backgroundColor = "#FF6600";
					grid[i].style.fontSize = "40px";
					break;
				case 1024:
					grid[i].style.backgroundColor = "#F53";
					grid[i].style.fontSize = "32px";
					break;
				case 2048:
					grid[i].style.backgroundColor = "#F40";
					grid[i].style.fontSize = "32px";
					break;
				default:
					grid[i].style.backgroundColor = "#b0c4de";

					// grid[i].children[0].style.color = "#fff";											
			}
		// }

	} 
}


// 游戲主入口
function getReady() {
	window.onkeydown = function(e) {
		deleteTempGrid();  // 在其他位置
		keyDown(e.keyCode);
		// backgroundColorToNumber();
	}
}

// getGridNum(ele):傳入div元素,返回格子裡面的數字
function getGridNum(ele) {
	return parseInt(ele.children[0].innerText);  // 空格返回NaN
}

// 各個方向的prevGrid,即所對應方向的前一個格子
function getPrevGrid(ele, direction) {
	var prevEle,
		count = 0;
	// 各個方向
	if (direction == "left") {
		return ele.previousElementSibling || null;
	} else if (direction == "right") {
		return ele.nextElementSibling || null;
	} else if (direction == "up") {
		for (var i = 0; i < 4; i ++) {
			ele = ele.previousElementSibling;
			if (!ele) {
				return null;
			}
		}
		return ele;
	} else if (direction == 'down') {
		for (var i = 0; i < 4; i ++) {
			ele = ele.nextElementSibling;
			if (!ele) {
				return null;
			}
		}
		return ele;
	}
}

// #滑塊移動#
// 桌面版通過監聽方向鍵來控制滑塊移動方向
function keyDown(keyCode) {
	var dir,
		arr,
		go,
		count = 0,   // 用於疊加每次運動得到的分數
		signal = 0;  // 用於判斷格子是否運動

	switch (keyCode) {
		case 37:
			dir = "left";
			break;
		case 38:
			dir = "up";
			break;
		case 39:
			dir = "right";
			break;
		case 40:
			dir = "down";
			break;
	}
	
	for (var i = 1; i < 5; i ++) {
		if (dir == "up" || dir == "down") {
			arr = document.getElementsByClassName("col" + i);
		}else if (dir == "left" || dir == "right") {
			arr = document.getElementsByClassName("row" + i);
		}
		if (dir == "up" || dir == "left") {
			for (var j = 1; j <= 3; j ++) {
				// console.log(col[j]);
				max = j;
				go = howToGo(arr[j], dir, max); // 疊加返回得分

				// console.log("go2:" + go);
				signal += go;
				if (go > 1) {
					count += go;   // 累計每一次運動的得分
				}
			}
		} else if (dir == "down" || dir == "right") {
			for (var j = 2; j >= 0; j --) {
				max = 3 - j;
				go = howToGo(arr[j], dir, max);
				// gridMove(arr[j], dir, 1);
				// console.log("go:" + go);
				signal += go;
				if (go > 1) {
					count += go;   // 累計每一次運動的得分
				}
			}
		}
		
	}
	// 格子有運動signal > 0
	if (signal > 0) {
		// console.log("signal:" + signal);
		giveNumber(2);
		backgroundColorToNumber();
		testGameOver();
	}
	// 格子移動,且得分>0
	if (count > 0) {
		addScore(count);
	}
	return count;
}

// 移動端使用touch事件來監聽滑塊移動
function touch() {
	var gameBoard = document.getElementsByClassName("game-board")[0];
	gameBoard.addEventListener("touchstart",function (e) {
		
		// e.preventDefault();
		startX = e.changedTouches[0].pageX;
	    startY = e.changedTouches[0].pageY;
	},false);

	gameBoard.addEventListener('touchend',function(e){
		
		e.preventDefault();  // 阻止瀏覽器的預設行為,例如滾動、跳轉等!!
		//獲取滑動屏幕時的X,Y
		endX = e.changedTouches[0].pageX,
		endY = e.changedTouches[0].pageY;
		//獲取滑動距離
		distanceX = endX-startX;
		distanceY = endY-startY;
		//判斷滑動方向,滑動角度大於15°
		if(Math.abs(distanceX) / Math.abs(distanceY) > 1.73 && distanceX > 0){
		    deleteTempGrid();
		    keyDown(39);
		}else if(Math.abs(distanceX) / Math.abs(distanceY) > 1.73 && distanceX < 0){
		    deleteTempGrid();
		    keyDown(37);
		}else if(Math.abs(distanceY) / Math.abs(distanceX) > 1.73 && distanceY < 0){
		    deleteTempGrid();
		    keyDown(38);
		}else if(Math.abs(distanceY) / Math.abs(distanceX) > 1.73 && distanceY > 0){
		    deleteTempGrid();
		    keyDown(40);
		}else{
		    // console.log('點擊未滑動');
		}
	});
}

// 3.記錄分數,分數會增加,
function addScore(score) {
	var span = document.getElementsByClassName("number"),
		currentScore = parseInt(span[0].innerText),
		bestScore = parseInt(span[1].innerText);
	span[0].innerText = score + currentScore;
	scoreUpAnimaton("score", score);
	if (span[0].innerText > bestScore) {
		scoreUpAnimaton("best", score);
		span[1].innerText = span[0].innerText;
	}
}

// howToGoLeft(ele, direction, max):該函數判斷單個格子怎麼移動
function howToGo(ele, direction, max, testMode) {
	var prevGrid,
		prevGridNum,
		gridNum = 0,
		go,
		addNum,
		numLen,
		doubleNumGrid;
		// console.log(prevGrid);

	// 各個方向
	prevGrid = getPrevGrid(ele, direction);
	gridNum = getGridNum(ele);
	if (prevGrid) {
		prevGridNum = getGridNum(prevGrid);
	} else {
		prevGridNum = "null";
	}
	// 前面是空格,要繼續判斷。。。。。。。。。。。。。。。。。。。。。
	if (gridNum && !prevGridNum) {
		prevGrid.innerHTML = ele.innerHTML;
		ele.children[0].innerText = "";
		max -= 1;
		// gridMove(ele, direction, 1);
		if (max) {
			go = howToGo(prevGrid, direction, max);
			// 0、double、continue
		}
		// 返回1
		// console.log("go:" + (go || 1));
		// if (max == 0) {
		// 	console.log("before:" + typeof(go));
		// 	go = 1;
		// 	console.log("after" + typeof(go));
		// }

		return go || 1; 
		// 若go = 0,返回1;go = double,返回double,go = underfied,返回1

	// 和前面數字相同
	} else if (gridNum == prevGridNum) {
		if (!testMode) {
			gridNum *= 2;
			// addScore(gridNum);
			// gridMove(ele, direction, 1);
			prevGrid.children[0].innerText = gridNum + "";
			
			// 在這裡添加數字變大的動畫:
			// numLen = (gridNum + "").length;

			ele.children[0].innerText = "";
			// console.log('gridNum:' + gridNum)
			if (gridNum == 2048) {
				popup("win");
			}
			// 如果數字疊加,就返回得分,且得分≥4
		}
		// console.log("gridNum:  " + gridNum);
		return gridNum;
	} else {
		// 格子沒動,返回0
		return 0;
	}
}


// 4.怎麼判斷game over,或者達到2048為winner
// test geme over
function testGameOver() {
	var content,
		leftTest,
		rightTest,
		upTest,
		downTest,
		count = 0;
		grid = document.getElementsByClassName("grid");
	for (var i = 0; i < grid.length; i ++) {
		content = grid[i].innerHTML;
		if (content != "<span></span>") {
			count += 1;
		}
	}
	// console.log("count:" + count);
	if (count == 16) {
		if (getGridNum(grid[3]) == getGridNum(grid[4])) {
			count -= 2;
		}
		if (getGridNum(grid[7]) == getGridNum(grid[8])) {
			count -= 2;
		}
		if (getGridNum(grid[11]) == getGridNum(grid[12])) {
			count -= 2;
		}
		for (var i = 0; i < grid.length; i ++) {
			if(!howToGo(grid[i], "left", 1, true) && !howToGo(grid[i], "right", 1, true) && !howToGo(grid[i], "up", 1, true) && !howToGo(grid[i], "down", 1, true)) {
				count --;
				if (count == 0) {
					popup("game-over");
					return true;
				}
			}
		}
	}
	return false;
}

// game over 後彈出
function popup(popType) {
	var num,
		tryAgainEle,
		ele = document.getElementsByClassName(popType)[0],
		headerEle = document.getElementsByClassName("header")[0],
		gameBoardEle = document.getElementsByClassName("game-board")[0];
	ele.style.display = "block";
	headerEle.style.opacity = "0.4";
	gameBoardEle.style.opacity = "0.4";

	// tryAgain(num);
	if (popType == "game-over") {
		num = 0;
	}
	if (popType == "win") {
		num = 1;
	}
	tryAgainEle = document.getElementsByClassName("try-again")[num];
	tryAgainEle.addEventListener("click", function () {
		tryAgain(ele, headerEle, gameBoardEle);
	}, false);
	tryAgainEle.addEventListener("touchend", function () {
		tryAgain(ele, headerEle, gameBoardEle);
	}, false);
}

// 再來一次
function tryAgain(ele, headerEle, gameBoardEle) {
	ele.style.display = "none";
	headerEle.style.opacity = "1.0";
	gameBoardEle.style.opacity = "1.0";
	newGame();
}

// 5.測試
function test() {
	var randomInt,
		timer;
	timer = setInterval(function() {
		randomInt = Math.floor(Math.random() * 4) + 37;
		keyDown(randomInt);
		// console.log(randomInt);
		if (testGameOver()) {
			clearInterval(timer);
		}
	}, 300);
}


// 分數增加的動畫
function scoreUpAnimaton(type, score) {
	var ele,
		score,
		timer,
		count = 0;
	if (type == "score") {
		ele = document.getElementsByClassName("score-animation")[0];
	} else if (type == "best") {
		ele = document.getElementsByClassName("best-animation")[0];
	}
	score = "+" + score;
	ele.innerText = score;
	ele.style.top = "25px";
	ele.style.color = "#8f7a66";
	ele.style.opacity = "1.0"

	timer = setInterval(function() {
		count ++;
		ele.style.display = "inline-block";
		ele.style.top = parseInt(ele.style.top) - 8 + "px";
		ele.style.opacity = parseFloat(ele.style.opacity) - 0.1;
		if (count == 6) {
			clearInterval(timer);
			ele.style.display = "none";
		}
	}, 80);
}

 

  

  


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、前言 在上一章的學習中,我們學習了 Vue 中組件的基礎知識,知道了什麼是組件,以及如何創建一個全局/局部組件。不知道你是否記得,在上一章中,我們提到組件是一個可以復用的 Vue 實例,它與 Vue 實例也只是擁有些許的差異。本章,我們將繼續學習組件的相關基礎知識,瞭解 Vue 的組件中的 da ...
  • 續前文: "線上代碼離線翻譯Chrome插件"一馬"v0.0.8" . 主要改進如下. 項目源碼庫: "program in chinese/webextension_github_code_translator" 添加基本詞庫 發現部分用戶安裝了"一馬"但未安裝離線英漢詞典插件, 這將導致翻譯完全 ...
  • "初めてのJavaScript 第3版 ES2015以降の最新ウェブ開発" . "示例代碼" : 譯者對採用日語命名的緣由在此闡述 "初めてのJavaScript 第3版 ES2015以降の最新ウェブ開発" (2016年12月): 翻訳の過程で、一部の例を日本語の識別子(変數名や関數名など)を使って ...
  • 前端 基礎教程 將放入菜單欄中,便於閱讀! 是`web JavaScript JavaScript`是必備的前端技能。 是用來描述網頁的結構, 是用來描述網頁的延時,而 是用來描述網頁的行為的。 是一種高端,動態,弱類型的編程語言。來源於 ,它的一等函數來源於 ,原型來源於 。 windwo.onl ...
  • IE 條件註釋判斷語句是 IE 特有的功能,通過 HTML 註釋中的條件語句能讓不同的 IE 版本識別註釋中的內容自IE10起,標準模式不再支持條件註釋 條件註釋語句中可以是HTML、CSS也可以是JavaScript,空格和大寫不要寫錯 1.只能被 IE 識別 <!--[if IE]> 只能被 I ...
  • 1.在IE6中png24格式的圖片不透明 解決辦法:寫一個條件註釋語句,引入一個js插件,然後調用一下js中的方法,把需要處理的元素的類名寫在括弧中,如下 (插件下載地址:http://www.dillerdesign.com/experiment/DD_belatedPNG/) 2.IE6中pos ...
  • this 指針問題 methods與computed中的this指針 應該指向的是它們自己,可是為什麼this指針卻可以訪問data對象中的成員呢? 因為new Vue對象實例化後data中的成員和computed中的成員為實現化對象屬性了,而methods中定義的方法為實現化對象方法了。這時thi ...
  • 本文主要介紹 vue的調試工具 vue-devtools 的安裝和使用 工欲善其事, 必先利其器, 快快一起來用vue-devtools來調試開發你的vue項目吧 安裝: 1.到github下載: 2.在vue-devtools目錄下安裝依賴包 1 2 cd vue-devtools cnpm in ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...