用原生JS写一个网页版的2048小游戏(兼容移动端)

时间:2022-03-14 16:07:58

 这个游戏JS部分全都是用原生JS代码写的,加有少量的CSS3动画,并简单的兼容了一下移动端。

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

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

用原生JS写一个网页版的2048小游戏(兼容移动端)

 

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

 

下面简单分析一下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两种情况碰到前面有格子时,说明前面的格子已经完成运动,前面没有空位了,所以当前格子运动完成,继续移动后面的格子。

 

以下使用图片来说明格子运动的情况:

用原生JS写一个网页版的2048小游戏(兼容移动端)

上面有两行格子,如果是向左运动,首先遍历第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号格子为空格,无需处理。

向左运动后的格子:

用原生JS写一个网页版的2048小游戏(兼容移动端)

 

如果是向右运动,遍历第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

向右运动后的格子:

用原生JS写一个网页版的2048小游戏(兼容移动端)

 

向上或者向下运动同理。

 

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

 

下面废话不说,直接上代码。

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);
}