欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2024-03-18 14:01:40
...

 这个游戏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);
}