html + css + js use HBuilder development tool to make 2048 puzzle game

2048–>html + css + js use HBuilder development tool to make 2048 puzzle game

Today, I used html + css + js to make a simple version of the 2048 puzzle game for the students. I hope that everyone will follow the steps and practice diligently, and set foot on the road to prosperity as soon as possible in the pit of coding. I wish you a bright future!

Effect picture:
insert image description here
attached source code github link:
https://github.com/LittleGreyKing-FriedBlackYoFeed-xoox/2048.git

Development ideas:

1. Use the HBuilder tool to create a blank html project.
insert image description here
When creating a project, try not to have a Chinese path in the path
insert image description here

2. Create alternate
insert image description here
insert image description here
project directories for js and css files:
insert image description here

3. Write html tags
Import css and js files:
insert image description here
Write html tags:
insert image description here
insert image description here
insert image description here
The code of html tags can be placed directly in the body tag:

index.html

<header>
			<h1>2048</h1>
		</header>
		<div id="gamePanel">
			<p><a class="button" id="newGame">New Game</a> Score: <span id="score">0</span><span class="writer">小灰灰版</span></p>
			<div id="gridPanel">
				<div class="grid"> </div>
				<div class="grid"> </div>
				<div class="grid"> </div>
				<div class="grid"> </div>

				<div class="grid"> </div>
				<div class="grid"> </div>
				<div class="grid"> </div>
				<div class="grid"> </div>

				<div class="grid"> </div>
				<div class="grid"> </div>
				<div class="grid"> </div>
				<div class="grid"> </div>

				<div class="grid"> </div>
				<div class="grid"> </div>
				<div class="grid"> </div>
				<div class="grid"> </div>

				<div class="cell " id="cell00"> </div>
				<div class="cell " id="cell01"> </div>
				<div class="cell " id="cell02"> </div>
				<div class="cell " id="cell03"> </div>

				<div class="cell " id="cell10"> </div>
				<div class="cell " id="cell11"> </div>
				<div class="cell " id="cell12"> </div>
				<div class="cell " id="cell13"> </div>

				<div class="cell " id="cell20"> </div>
				<div class="cell " id="cell21"> </div>
				<div class="cell " id="cell22"> </div>
				<div class="cell " id="cell23"> </div>

				<div class="cell " id="cell30"> </div>
				<div class="cell " id="cell31"> </div>
				<div class="cell " id="cell32"> </div>
				<div class="cell " id="cell33"> </div>

			</div>
			<div id="gameOver">
				<div>
					<!--背景 -->
				</div>
				<p>
					<!-- 前景 -->
					Game Over!<br>
					Score: <span id="finalScore">0</span><br>
					<a class="button" id="restart">Try Again!</a>
				</p>
			</div>
		</div>

Fourth, draw the css style sheet
insert image description here
2048.css


* {
    
    
	padding: 0;
	margin: 0;

}

div,
p,
h1,
h2,
a,
span {
    
    
	font-family: Arial;
	font-weight: bold;
}

header {
    
    
	padding-top: 16px;
}

h1 {
    
    
	font-size: 40px;
	font-weight: bold;
	text-align: center;
}

#gamePanel {
    
    
	margin: 0 auto;
	width: 500px;
	position: relative;
}

#gridPanel {
    
    
	width: 480px;
	height: 480px;
	background: #BBADA0;
	border-radius: 10px;
	padding: 20px 0 0 20px;
	position: relative;
}

#gamePanel p {
    
    
	padding: 8px;
}

/* 按钮样式 */
.button {
    
    
	display: inline-block;
	padding: 10px;
	background: #9F8B77;
	border-radius: 6px;
	color: #FFF;
	cursor: pointer;
}

.grid,
.cell {
    
    
	width: 100px;
	height: 100px;
	border-radius: 6px;
}

/* 背景网格 */
#gamePanel .grid {
    
    
	background-color: #ccc0b3;
	float: left;
	margin: 0 20px 20px 0;
}

/* 格式化前景单元格中位置 */
.cell {
    
    
	position: absolute;
	line-height: 100px;
	vertical-align: middle;
	text-align: center;
	font-size: 60px;
	color: #776E65;
}

/* 前景格中的行位置 */
#cell00,
#cell01,
#cell02,
#cell03 {
    
    
	top: 20;
}

#cell10,
#cell11,
#cell12,
#cell13 {
    
    
	top: 140px;
}

#cell20,
#cell21,
#cell22,
#cell23 {
    
    
	top: 260px;
}

#cell30,
#cell31,
#cell32,
#cell33 {
    
    
	top: 380px;
}

/* 前景格中的列位置 */
#cell00,
#cell10,
#cell20,
#cell30 {
    
    
	left: 20px;
}

#cell01,
#cell11,
#cell21,
#cell31 {
    
    
	left: 140px;
}

#cell02,
#cell12,
#cell22,
#cell32 {
    
    
	left: 260px;
}

#cell03,
#cell13,
#cell23,
#cell33 {
    
    
	left: 380px;
}

/* 数字显示效果 */
.num8,
.num16,
.num32,
.num64,
.num128,
.num256,
.num512,
.num1024,
.num2048,
.num4096,
.num8192 {
    
    
	color: #fff;
}

.num1024,
.num2048,
.num4096,
.num8192 {
    
    
	font-size: 40px;
}

.num2 {
    
    
	background: #eee4da;
}

.num4 {
    
    
	background: #ede0c8;
}

.num8 {
    
    
	background: #f2b179;
}

.num16 {
    
    
	background: #f59563;
}

.num32 {
    
    
	background: #f67c5f;
}

.num64 {
    
    
	background: #f65e3b;
}

.num128 {
    
    
	background: #edcf72;
}

.num256 {
    
    
	background: #edcc61;
}

.num512 {
    
    
	background: #9c0;
}

.num1024 {
    
    
	background: #33b5e5;
}

.num2048 {
    
    
	background: #09c;
}

.num4096 {
    
    
	background: #a6c;
}

.num8192 {
    
    
	background: #93c;
}

/* 格式化Game Over的样式 */
#gameOver {
    
    
	display: none;
	position: absolute;
	width: 100%;
	height: 100%;
	top: 0;
	left: 0;
}

#gameOver div {
    
    
	width: 100%;
	height: 100%;
	background: #555;
	filter: alpha(Opacity=50);
	-moz-opacity: 0.5;
	opacity: 0.5;
}

#gameOver p {
    
    
	position: absolute;
	top: 150px;
	left: 100px;
	border-radius: 10px;
	width: 300px;
	border: 1px solid #EDCF72;
	background: #fff;
	line-height: 1.6em;
	font-size: 30px;
	color: #000;
	text-align: center;
}

.writer {
    
    
	width: 80px;
	margin-left: 30%;
}

5. Create JS event function

insert image description here
animation.js

    	//动画效果工具对象
    	var animation = {
    
    
    		tasks:[], //动画任务数据
    		timer:null,//动画定时器
			times:10, //定时器次数
			interval:1000/60,//定时器间隔
			add:function(obj, top, left){
    
    
				
				//动画已经开始了,不能添加任务了!
				if(this.timer){
    
    
					return false;
				}
				if(! obj){
    
    
					return false;
				}
				
				//计算移动步伐
				var t = (top - obj.offsetTop)/this.times;
				var l = (left - obj.offsetLeft)/this.times;
				
				//添加动画任务,其中obj是被移动到对象,top, left是移动以后的位置
				var task = {
    
    
					topStep:t,
					leftStep:l,
					element:obj,	
					step:function(){
    
    
						var t = this.element.offsetTop;
						this.element.style.top = (t+ this.topStep )+"px";
						var l = this.element.offsetLeft;
						this.element.style.left = (l+ this.leftStep )+"px";		
					},
					clear:function(){
    
    
						this.element.style.top = "";
						this.element.style.left = "";
					}
				};
				this.tasks[this.tasks.length]=task; 
				return true;
			},
    		start:function(callback){
    
    
    			//如果定时器已经启动,就不能再启动定时器了
    			if(this.timer){
    
    
    				return false;
    			}

				console.log("STATRING");
				if(this.tasks.length==0){
    
    
					if(callback){
    
    
						callback();
					}
					return false;	
				}
				
				//如果有callback就交给this.callback
				if(callback){
    
    
					this.callback = callback;
				}
				this.timer = setInterval(function(){
    
    
					//console.log("timeOut");
					//console.log(animation.times);
					
					for(var i=0; i<animation.tasks.length; i++){
    
    
						var task = animation.tasks[i];
						task.step();
					}
					
					animation.times--;
					if(animation.times < 0){
    
    
						animation.stop();
					}
				}, this.interval);
				return true;
    		},
    		stop: function(){
    
    
    			if(this.timer){
    
    
    				window.clearInterval(this.timer);
    				this.timer=null;
    				this.times=10;
    				
    			}
    			
    			//结束以后执行callback()
    			if(this.callback){
    
    
    				this.callback();
    			}
				for(var i=0; i<this.tasks.length; i++){
    
    
					var task = this.tasks[i];
					task.clear();
				}    			
				this.tasks = [];//清空动画任务
    		},
    		callback:null //动画结束时候执行的方法
    	};

The JS part is also included in index.html:
insert image description here
js part included in index.html

<script type="text/javascript">
			//game 封装了2048的数据和核心算法
			var cells = [
				[2, 0, 0, 0],
				[0, 32, 0, 0],
				[0, 4, 0, 0],
				[0, 0, 2048, 0]
			];
			//游戏进行中
			var PLAYING = 0;
			//方块正在移动动画处理中,期间不能响应键盘事件
			var CELL_MOVEING = 1;
			//游戏结束了,结束了就不能响应键盘事件了
			var GAME_OVER = 2;

			var score = 0;

			//当前游戏状态
			var state = PLAYING;

			//动态效果开关,打开后可以绘制方块的移动动画效果
			var effect = true;

			/* 向上的动作, */
			function upAction() {
    
    
				if (state == CELL_MOVEING) {
    
    
					return false;
				}
				if (!canMoveUp()) {
    
    
					return false;
				}
				//每次处理一个列
				for (var col = 0; col < 4; col++) {
    
    
					//每一个列中 从放方向判断是否需要移动处理
					upCol(col);
				}
				return true;
			}
			// 处理一个列的移动 
			function upCol(col) {
    
    
				//一个列中,按照方方向检查是否需要合并处理。
				for (var row = 0; row < 4;) {
    
    
					var current = cells[row][col];
					var nextRow = getNextInCol(col, row + 1, 1);
					//没有下一个,就直接结束了
					if (nextRow == -1) {
    
    
						return;
					}
					var next = cells[nextRow][col];

					if (current == 0) {
    
    
						//下一个格子移动到当前位置。
						cells[row][col] = next;
						cells[nextRow][col] = 0;
						if (effect) {
    
     //如果动效开关打开,就处理动画
							// $("cell"+nextRow+col) 找到下一个对象对应的格子
							var obj = $("cell" + nextRow + col);
							var top = row * 120 + 20;
							var left = col * 120 + 20;
							animation.add(obj, top, left);
						}
					} else if (current == next) {
    
    
						//两个格子一样,就合并格子
						cells[row][col] = next + current;
						cells[nextRow][col] = 0;

						score += cells[row][col];

						row++;
						if (effect) {
    
     //如果动效开关打开,就处理动画
							// $("cell"+nextRow+col) 找到下一个对象对应的格子
							var obj = $("cell" + nextRow + col);
							var top = row * 120 + 20;
							var left = col * 120 + 20;
							animation.add(obj, top, left);
						}
					} else {
    
    
						//下个不一样,就忽略之
						row++;
					}
				}
			}

			function getNextInCol(col, startRow, step) {
    
    
				var row = startRow;
				while (true) {
    
    
					if (row < 0 || row >= 4) {
    
    
						return -1;
					}
					if (cells[row][col] != 0) {
    
    
						return row;
					}
					row += step;
				}
			}

			function getNextInRow(row, startCol, step) {
    
    
				var col = startCol;
				while (true) {
    
    
					if (col < 0 || col >= 4) {
    
    
						return -1;
					}
					if (cells[row][col] != 0) {
    
    
						return col;
					}
					col += step;
				}
			}

			function downAction() {
    
    
				if (state == CELL_MOVEING) {
    
    
					return false;
				}
				if (!canMoveDown()) {
    
    
					return false;
				}
				//每次处理一个列
				for (var col = 0; col < 4; col++) {
    
    
					//每一个列中 从放方向判断是否需要移动处理
					downCol(col);
				}
				return true;
			}
			// 处理一个列的移动
			function downCol(col) {
    
    
				//一个列中,按照方方向检查是否需要合并处理。
				for (var row = 3; row >= 0;) {
    
    
					var current = cells[row][col];
					var nextRow = getNextInCol(col, row - 1, -1);
					//没有下一个,就直接结束了
					if (nextRow == -1) {
    
    
						return;
					}
					var next = cells[nextRow][col];

					if (current == 0) {
    
    
						//下一个格子移动到当前位置。
						cells[row][col] = next;
						cells[nextRow][col] = 0;
						if (effect) {
    
     //如果动效开关打开,就处理动画
							// $("cell"+nextRow+col) 找到下一个对象对应的格子
							var obj = $("cell" + nextRow + col);
							var top = row * 120 + 20;
							var left = col * 120 + 20;
							animation.add(obj, top, left);
						}
					} else if (current == next) {
    
    
						//两个格子一样,就合并格子
						cells[row][col] = next + current;
						cells[nextRow][col] = 0;

						score += cells[row][col];

						row--;

						if (effect) {
    
     //如果动效开关打开,就处理动画
							// $("cell"+nextRow+col) 找到下一个对象对应的格子
							var obj = $("cell" + nextRow + col);
							var top = row * 120 + 20;
							var left = col * 120 + 20;
							animation.add(obj, top, left);
						}
					} else {
    
    
						//下个不一样,就忽略之
						row--;
					}
				}
			}

			function leftAction() {
    
    
				if (state == CELL_MOVEING) {
    
    
					return false;
				}
				if (!canMoveLeft()) {
    
    
					return false;
				}
				//每次处理一个行
				for (var row = 0; row < 4; row++) {
    
    
					//每一个行中 从放方向判断是否需要移动处理
					moveLeft(row);
				}
				return true;
			}
			// 处理一个列的移动
			function moveLeft(row) {
    
    
				//一个列中,按照方方向检查是否需要合并处理。
				for (var col = 0; col < 4;) {
    
    
					var current = cells[row][col];
					var nextCol = getNextInRow(row, col + 1, 1);
					//没有下一个,就直接结束了
					if (nextCol == -1) {
    
    
						return;
					}
					var next = cells[row][nextCol];

					//console.log("next:"+next);
					//console.log("current:"+current);

					if (current == 0) {
    
    
						//下一个格子移动到当前位置。
						cells[row][col] = next;
						cells[row][nextCol] = 0;
						if (effect) {
    
     //如果动效开关打开,就处理动画
							// $("cell"+nextRow+col) 找到下一个对象对应的格子
							var obj = $("cell" + row + nextCol);
							var top = row * 120 + 20;
							var left = col * 120 + 20;
							animation.add(obj, top, left);
						}
					} else if (current == next) {
    
    
						//两个格子一样,就合并格子
						cells[row][col] = next + current;
						cells[row][nextCol] = 0;

						score += cells[row][col];

						col++;
						if (effect) {
    
     //如果动效开关打开,就处理动画
							// $("cell"+nextRow+col) 找到下一个对象对应的格子
							var obj = $("cell" + row + nextCol);
							var top = row * 120 + 20;
							var left = col * 120 + 20;
							animation.add(obj, top, left);
						}
					} else {
    
    
						//下个不一样,就忽略之
						col++;
					}
				}
			}

			function rightAction() {
    
    
				if (state == CELL_MOVEING) {
    
    
					return false;
				}
				if (!canMoveRight()) {
    
    
					return false;
				}
				//每次处理一个行
				for (var row = 0; row < 4; row++) {
    
    
					//每一个行中 从放方向判断是否需要移动处理
					moveRight(row);
				}
				return true;
			}
			// 处理一个列的移动
			function moveRight(row) {
    
    
				//一个列中,按照方方向检查是否需要合并处理。
				for (var col = 3; col >= 0;) {
    
    
					var current = cells[row][col];
					var nextCol = getNextInRow(row, col - 1, -1);
					//没有下一个,就直接结束了
					if (nextCol == -1) {
    
    
						return;
					}
					var next = cells[row][nextCol];

					//console.log("next:"+next);
					//console.log("current:"+current);

					if (current == 0) {
    
    
						//下一个格子移动到当前位置。
						cells[row][col] = next;
						cells[row][nextCol] = 0;
						if (effect) {
    
     //如果动效开关打开,就处理动画
							// $("cell"+nextRow+col) 找到下一个对象对应的格子
							var obj = $("cell" + row + nextCol);
							var top = row * 120 + 20;
							var left = col * 120 + 20;
							animation.add(obj, top, left);
						}
					} else if (current == next) {
    
    
						//两个格子一样,就合并格子
						cells[row][col] = next + current;
						cells[row][nextCol] = 0;


						score += cells[row][col];

						col--;

						if (effect) {
    
     //如果动效开关打开,就处理动画
							// $("cell"+nextRow+col) 找到下一个对象对应的格子
							var obj = $("cell" + row + nextCol);
							var top = row * 120 + 20;
							var left = col * 120 + 20;
							animation.add(obj, top, left);
						}
					} else {
    
    
						//下个不一样,就忽略之
						col--;
					}
				}
			}

			function canMoveUp() {
    
    
				for (var col = 0; col < 4; col++) {
    
    
					for (var row = 1; row < 4; row++) {
    
    
						//格子上方是空位置, 可以移动
						if (cells[row][col] != 0 && cells[row - 1][col] == 0) {
    
    
							return true;
						}
						//格子上方相邻的相等,可以移动
						if (cells[row][col] != 0 && (cells[row][col] == cells[row - 1][col])) {
    
    
							return true;
						}
					}
				}
				return false;
			}

			function canMoveDown() {
    
    
				for (var col = 0; col < 4; col++) {
    
    
					for (var row = 0; row < 3; row++) {
    
    
						//格子上方是空位置, 可以移动
						if (cells[row][col] != 0 && cells[row + 1][col] == 0) {
    
    
							return true;
						}
						//格子上方相邻的相等,可以移动
						if (cells[row][col] != 0 && (cells[row][col] == cells[row + 1][col])) {
    
    
							return true;
						}
					}
				}
				return false;
			}

			function canMoveLeft() {
    
    
				for (var col = 1; col < 4; col++) {
    
    
					for (var row = 0; row < 4; row++) {
    
    
						//格子上方是空位置, 可以移动
						if (cells[row][col] != 0 && cells[row][col - 1] == 0) {
    
    
							return true;
						}
						//格子上方相邻的相等,可以移动
						if (cells[row][col] != 0 && (cells[row][col] == cells[row][col - 1])) {
    
    
							return true;
						}
					}
				}
				return false;
			}

			function canMoveRight() {
    
    
				for (var col = 0; col < 3; col++) {
    
    
					for (var row = 0; row < 4; row++) {
    
    
						//格子上方是空位置, 可以移动
						if (cells[row][col] != 0 && cells[row][col + 1] == 0) {
    
    
							return true;
						}
						//格子上方相邻的相等,可以移动
						if (cells[row][col] != 0 && (cells[row][col] == cells[row][col + 1])) {
    
    
							return true;
						}
					}
				}
				return false;
			}


			function test() {
    
    
				rightAction();
				state = CELL_MOVEING;
				animation.start(function() {
    
    
					//console.log("update");
					randomNumber();
					updateView();
					state = PLAYING;
				});
			}

			//更新显示,将表格中的数据,更新到界面显示
			function updateView() {
    
    
				for (var row = 0; row < 4; row++) {
    
    
					for (var col = 0; col < 4; col++) {
    
    
						var n = cells[row][col];
						var cell = $("cell" + row + col);
						//清楚显示的数据和显示样式
						cell.className = "cell";
						cell.innerHTML = "";
						if (n > 0) {
    
    
							//更新显示样式
							cell.className = "cell num" + n;
							//更新显示的数字
							cell.innerHTML = n;
						}
					}
				}

				$("score").innerHTML = score;
				$("finalScore").innerHTML = score;

			}
			//检查当前的表格中是否是满的,如果满了返回true,否则返回false
			function full() {
    
    
				for (var row = 0; row < 4; row++) {
    
    
					for (var col = 0; col < 4; col++) {
    
    
						if (cells[row][col] == 0) {
    
    
							return false;
						}
					}
				}
				return true;
			}
			//向表格随机插入一个数字,如果插入成功返回true,插入失败返回false
			function randomNumber() {
    
    
				if (full()) {
    
    
					return false;
				}
				while (true) {
    
    
					var col = parseInt(Math.random() * 4);
					var row = parseInt(Math.random() * 4);
					if (cells[row][col] == 0) {
    
    
						var n = Math.random() < 0.5 ? 2 : 4;
						cells[row][col] = n;
						return true;
					}
				}
			}

			function startAction() {
    
    
				$("gameOver").style.display = "none";
				for (var row = 0; row < 4; row++) {
    
    
					for (var col = 0; col < 4; col++) {
    
    
						cells[row][col] = 0;
					}
				}
				score = 0;
				randomNumber();
				randomNumber();
				updateView();
				state = PLAYING;
			}
			//元素查询方法
			function $(id) {
    
    
				return document.getElementById(id);
			}

			function has8192() {
    
    
				for (var row = 0; row < 4; row++) {
    
    
					for (var col = 0; col < 4; col++) {
    
    
						if (cells[row][col] == 8192) {
    
    
							return true;
						}
					}
				}
			}

			function hasSpace() {
    
    
				for (var row = 0; row < 4; row++) {
    
    
					for (var col = 0; col < 4; col++) {
    
    
						if (cells[row][col] == 0) {
    
    
							return true;
						}
					}
				}
			}


			/* 显示游戏结束界面 */
			function gameOver() {
    
    
				//发现8192游戏结束
				if (has8192()) {
    
    
					state = GAME_OVER;
					$("gameOver").style.display = "block";
					return true;
				}

				//发现空位置,游戏不结束    	
				if (hasSpace()) {
    
    
					return false;
				}

				//能够移动游戏不结束
				if (canMoveUp() || canMoveDown() || canMoveLeft() || canMoveRight()) {
    
    
					return false;
				}
				state = GAME_OVER;
				$("gameOver").style.display = "block";
				return true;
			}

			//软件启动初始代码
			window.onload = function() {
    
    
				$("newGame").onclick = function() {
    
    
					if (state == PLAYING)
						startAction();
				}
				$("restart").onclick = function() {
    
    
					if (state == GAME_OVER)
						startAction();
				}
				startAction();
				//监听键盘事件
				document.onkeydown = function(event) {
    
    
					if (state != PLAYING) {
    
    
						return;
					}
					var move = false;
					switch (event.keyCode) {
    
    
						case 37: //left
							move = leftAction();
							break;
						case 38: //up
							move = upAction();
							break;
						case 39: //right
							move = rightAction();
							break;
						case 40: //down
							move = downAction();
							break;
					}
					if (!move) {
    
    
						return;
					}
					if (effect) {
    
    
						state = CELL_MOVEING;
						animation.start(function() {
    
    
							//console.log("update");
							updateView();
							state = PLAYING;
							if (!gameOver()) {
    
    
								setTimeout(function() {
    
    
									randomNumber();
									updateView();
								}, 100);
							}
						});
					} else {
    
    
						if (!gameOver()) {
    
    
							setTimeout(function() {
    
    
								randomNumber();
								updateView();
							}, 100);
						}

						updateView();
						state = PLAYING;
					}
					gameOver();
				}; // 5211 0842 8479 快捷快递
			}
		</script>

6. Test and run

When configuring and running, remember to open index.html before executing.
insert image description here
insert image description here
insert image description here
insert image description here
complete.

7. All source code of lazy version

Many students like to put html, css and js in the same file. I also made a lazy version of 2048 for you. The source code is provided below:

2048.html

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title>2048-懒人版</title>
		<!-- 2048.css -->
		<style type="text/css">
			* {
    
    
				padding: 0;
				margin: 0;
			
			}
			
			div,
			p,
			h1,
			h2,
			a,
			span {
    
    
				font-family: Arial;
				font-weight: bold;
			}
			
			header {
    
    
				padding-top: 16px;
			}
			
			h1 {
    
    
				font-size: 40px;
				font-weight: bold;
				text-align: center;
			}
			
			#gamePanel {
    
    
				margin: 0 auto;
				width: 500px;
				position: relative;
			}
			
			#gridPanel {
    
    
				width: 480px;
				height: 480px;
				background: #BBADA0;
				border-radius: 10px;
				padding: 20px 0 0 20px;
				position: relative;
			}
			
			#gamePanel p {
    
    
				padding: 8px;
			}
			
			/* 按钮样式 */
			.button {
    
    
				display: inline-block;
				padding: 10px;
				background: #9F8B77;
				border-radius: 6px;
				color: #FFF;
				cursor: pointer;
			}
			
			.grid,
			.cell {
    
    
				width: 100px;
				height: 100px;
				border-radius: 6px;
			}
			
			/* 背景网格 */
			#gamePanel .grid {
    
    
				background-color: #ccc0b3;
				float: left;
				margin: 0 20px 20px 0;
			}
			
			/* 格式化前景单元格中位置 */
			.cell {
    
    
				position: absolute;
				line-height: 100px;
				vertical-align: middle;
				text-align: center;
				font-size: 60px;
				color: #776E65;
			}
			
			/* 前景格中的行位置 */
			#cell00,
			#cell01,
			#cell02,
			#cell03 {
    
    
				top: 20;
			}
			
			#cell10,
			#cell11,
			#cell12,
			#cell13 {
    
    
				top: 140px;
			}
			
			#cell20,
			#cell21,
			#cell22,
			#cell23 {
    
    
				top: 260px;
			}
			
			#cell30,
			#cell31,
			#cell32,
			#cell33 {
    
    
				top: 380px;
			}
			
			/* 前景格中的列位置 */
			#cell00,
			#cell10,
			#cell20,
			#cell30 {
    
    
				left: 20px;
			}
			
			#cell01,
			#cell11,
			#cell21,
			#cell31 {
    
    
				left: 140px;
			}
			
			#cell02,
			#cell12,
			#cell22,
			#cell32 {
    
    
				left: 260px;
			}
			
			#cell03,
			#cell13,
			#cell23,
			#cell33 {
    
    
				left: 380px;
			}
			
			/* 数字显示效果 */
			.num8,
			.num16,
			.num32,
			.num64,
			.num128,
			.num256,
			.num512,
			.num1024,
			.num2048,
			.num4096,
			.num8192 {
    
    
				color: #fff;
			}
			
			.num1024,
			.num2048,
			.num4096,
			.num8192 {
    
    
				font-size: 40px;
			}
			
			.num2 {
    
    
				background: #eee4da;
			}
			
			.num4 {
    
    
				background: #ede0c8;
			}
			
			.num8 {
    
    
				background: #f2b179;
			}
			
			.num16 {
    
    
				background: #f59563;
			}
			
			.num32 {
    
    
				background: #f67c5f;
			}
			
			.num64 {
    
    
				background: #f65e3b;
			}
			
			.num128 {
    
    
				background: #edcf72;
			}
			
			.num256 {
    
    
				background: #edcc61;
			}
			
			.num512 {
    
    
				background: #9c0;
			}
			
			.num1024 {
    
    
				background: #33b5e5;
			}
			
			.num2048 {
    
    
				background: #09c;
			}
			
			.num4096 {
    
    
				background: #a6c;
			}
			
			.num8192 {
    
    
				background: #93c;
			}
			
			/* 格式化Game Over的样式 */
			#gameOver {
    
    
				display: none;
				position: absolute;
				width: 100%;
				height: 100%;
				top: 0;
				left: 0;
			}
			
			#gameOver div {
    
    
				width: 100%;
				height: 100%;
				background: #555;
				filter: alpha(Opacity=50);
				-moz-opacity: 0.5;
				opacity: 0.5;
			}
			
			#gameOver p {
    
    
				position: absolute;
				top: 150px;
				left: 100px;
				border-radius: 10px;
				width: 300px;
				border: 1px solid #EDCF72;
				background: #fff;
				line-height: 1.6em;
				font-size: 30px;
				color: #000;
				text-align: center;
			}
			
			.writer {
    
    
				width: 80px;
				margin-left: 30%;
			}
			
		</style>
		<script type="text/javascript">
			//game 封装了2048的数据和核心算法
			var cells = [
				[2, 0, 0, 0],
				[0, 32, 0, 0],
				[0, 4, 0, 0],
				[0, 0, 2048, 0]
			];
			//游戏进行中
			var PLAYING = 0;
			//方块正在移动动画处理中,期间不能响应键盘事件
			var CELL_MOVEING = 1;
			//游戏结束了,结束了就不能响应键盘事件了
			var GAME_OVER = 2;

			var score = 0;

			//当前游戏状态
			var state = PLAYING;

			//动态效果开关,打开后可以绘制方块的移动动画效果
			var effect = true;

			/* 向上的动作, */
			function upAction() {
    
    
				if (state == CELL_MOVEING) {
    
    
					return false;
				}
				if (!canMoveUp()) {
    
    
					return false;
				}
				//每次处理一个列
				for (var col = 0; col < 4; col++) {
    
    
					//每一个列中 从放方向判断是否需要移动处理
					upCol(col);
				}
				return true;
			}
			// 处理一个列的移动 
			function upCol(col) {
    
    
				//一个列中,按照方方向检查是否需要合并处理。
				for (var row = 0; row < 4;) {
    
    
					var current = cells[row][col];
					var nextRow = getNextInCol(col, row + 1, 1);
					//没有下一个,就直接结束了
					if (nextRow == -1) {
    
    
						return;
					}
					var next = cells[nextRow][col];

					if (current == 0) {
    
    
						//下一个格子移动到当前位置。
						cells[row][col] = next;
						cells[nextRow][col] = 0;
						if (effect) {
    
     //如果动效开关打开,就处理动画
							// $("cell"+nextRow+col) 找到下一个对象对应的格子
							var obj = $("cell" + nextRow + col);
							var top = row * 120 + 20;
							var left = col * 120 + 20;
							animation.add(obj, top, left);
						}
					} else if (current == next) {
    
    
						//两个格子一样,就合并格子
						cells[row][col] = next + current;
						cells[nextRow][col] = 0;

						score += cells[row][col];

						row++;
						if (effect) {
    
     //如果动效开关打开,就处理动画
							// $("cell"+nextRow+col) 找到下一个对象对应的格子
							var obj = $("cell" + nextRow + col);
							var top = row * 120 + 20;
							var left = col * 120 + 20;
							animation.add(obj, top, left);
						}
					} else {
    
    
						//下个不一样,就忽略之
						row++;
					}
				}
			}

			function getNextInCol(col, startRow, step) {
    
    
				var row = startRow;
				while (true) {
    
    
					if (row < 0 || row >= 4) {
    
    
						return -1;
					}
					if (cells[row][col] != 0) {
    
    
						return row;
					}
					row += step;
				}
			}

			function getNextInRow(row, startCol, step) {
    
    
				var col = startCol;
				while (true) {
    
    
					if (col < 0 || col >= 4) {
    
    
						return -1;
					}
					if (cells[row][col] != 0) {
    
    
						return col;
					}
					col += step;
				}
			}

			function downAction() {
    
    
				if (state == CELL_MOVEING) {
    
    
					return false;
				}
				if (!canMoveDown()) {
    
    
					return false;
				}
				//每次处理一个列
				for (var col = 0; col < 4; col++) {
    
    
					//每一个列中 从放方向判断是否需要移动处理
					downCol(col);
				}
				return true;
			}
			// 处理一个列的移动
			function downCol(col) {
    
    
				//一个列中,按照方方向检查是否需要合并处理。
				for (var row = 3; row >= 0;) {
    
    
					var current = cells[row][col];
					var nextRow = getNextInCol(col, row - 1, -1);
					//没有下一个,就直接结束了
					if (nextRow == -1) {
    
    
						return;
					}
					var next = cells[nextRow][col];

					if (current == 0) {
    
    
						//下一个格子移动到当前位置。
						cells[row][col] = next;
						cells[nextRow][col] = 0;
						if (effect) {
    
     //如果动效开关打开,就处理动画
							// $("cell"+nextRow+col) 找到下一个对象对应的格子
							var obj = $("cell" + nextRow + col);
							var top = row * 120 + 20;
							var left = col * 120 + 20;
							animation.add(obj, top, left);
						}
					} else if (current == next) {
    
    
						//两个格子一样,就合并格子
						cells[row][col] = next + current;
						cells[nextRow][col] = 0;

						score += cells[row][col];

						row--;

						if (effect) {
    
     //如果动效开关打开,就处理动画
							// $("cell"+nextRow+col) 找到下一个对象对应的格子
							var obj = $("cell" + nextRow + col);
							var top = row * 120 + 20;
							var left = col * 120 + 20;
							animation.add(obj, top, left);
						}
					} else {
    
    
						//下个不一样,就忽略之
						row--;
					}
				}
			}

			function leftAction() {
    
    
				if (state == CELL_MOVEING) {
    
    
					return false;
				}
				if (!canMoveLeft()) {
    
    
					return false;
				}
				//每次处理一个行
				for (var row = 0; row < 4; row++) {
    
    
					//每一个行中 从放方向判断是否需要移动处理
					moveLeft(row);
				}
				return true;
			}
			// 处理一个列的移动
			function moveLeft(row) {
    
    
				//一个列中,按照方方向检查是否需要合并处理。
				for (var col = 0; col < 4;) {
    
    
					var current = cells[row][col];
					var nextCol = getNextInRow(row, col + 1, 1);
					//没有下一个,就直接结束了
					if (nextCol == -1) {
    
    
						return;
					}
					var next = cells[row][nextCol];

					//console.log("next:"+next);
					//console.log("current:"+current);

					if (current == 0) {
    
    
						//下一个格子移动到当前位置。
						cells[row][col] = next;
						cells[row][nextCol] = 0;
						if (effect) {
    
     //如果动效开关打开,就处理动画
							// $("cell"+nextRow+col) 找到下一个对象对应的格子
							var obj = $("cell" + row + nextCol);
							var top = row * 120 + 20;
							var left = col * 120 + 20;
							animation.add(obj, top, left);
						}
					} else if (current == next) {
    
    
						//两个格子一样,就合并格子
						cells[row][col] = next + current;
						cells[row][nextCol] = 0;

						score += cells[row][col];

						col++;
						if (effect) {
    
     //如果动效开关打开,就处理动画
							// $("cell"+nextRow+col) 找到下一个对象对应的格子
							var obj = $("cell" + row + nextCol);
							var top = row * 120 + 20;
							var left = col * 120 + 20;
							animation.add(obj, top, left);
						}
					} else {
    
    
						//下个不一样,就忽略之
						col++;
					}
				}
			}

			function rightAction() {
    
    
				if (state == CELL_MOVEING) {
    
    
					return false;
				}
				if (!canMoveRight()) {
    
    
					return false;
				}
				//每次处理一个行
				for (var row = 0; row < 4; row++) {
    
    
					//每一个行中 从放方向判断是否需要移动处理
					moveRight(row);
				}
				return true;
			}
			// 处理一个列的移动
			function moveRight(row) {
    
    
				//一个列中,按照方方向检查是否需要合并处理。
				for (var col = 3; col >= 0;) {
    
    
					var current = cells[row][col];
					var nextCol = getNextInRow(row, col - 1, -1);
					//没有下一个,就直接结束了
					if (nextCol == -1) {
    
    
						return;
					}
					var next = cells[row][nextCol];

					//console.log("next:"+next);
					//console.log("current:"+current);

					if (current == 0) {
    
    
						//下一个格子移动到当前位置。
						cells[row][col] = next;
						cells[row][nextCol] = 0;
						if (effect) {
    
     //如果动效开关打开,就处理动画
							// $("cell"+nextRow+col) 找到下一个对象对应的格子
							var obj = $("cell" + row + nextCol);
							var top = row * 120 + 20;
							var left = col * 120 + 20;
							animation.add(obj, top, left);
						}
					} else if (current == next) {
    
    
						//两个格子一样,就合并格子
						cells[row][col] = next + current;
						cells[row][nextCol] = 0;


						score += cells[row][col];

						col--;

						if (effect) {
    
     //如果动效开关打开,就处理动画
							// $("cell"+nextRow+col) 找到下一个对象对应的格子
							var obj = $("cell" + row + nextCol);
							var top = row * 120 + 20;
							var left = col * 120 + 20;
							animation.add(obj, top, left);
						}
					} else {
    
    
						//下个不一样,就忽略之
						col--;
					}
				}
			}

			function canMoveUp() {
    
    
				for (var col = 0; col < 4; col++) {
    
    
					for (var row = 1; row < 4; row++) {
    
    
						//格子上方是空位置, 可以移动
						if (cells[row][col] != 0 && cells[row - 1][col] == 0) {
    
    
							return true;
						}
						//格子上方相邻的相等,可以移动
						if (cells[row][col] != 0 && (cells[row][col] == cells[row - 1][col])) {
    
    
							return true;
						}
					}
				}
				return false;
			}

			function canMoveDown() {
    
    
				for (var col = 0; col < 4; col++) {
    
    
					for (var row = 0; row < 3; row++) {
    
    
						//格子上方是空位置, 可以移动
						if (cells[row][col] != 0 && cells[row + 1][col] == 0) {
    
    
							return true;
						}
						//格子上方相邻的相等,可以移动
						if (cells[row][col] != 0 && (cells[row][col] == cells[row + 1][col])) {
    
    
							return true;
						}
					}
				}
				return false;
			}

			function canMoveLeft() {
    
    
				for (var col = 1; col < 4; col++) {
    
    
					for (var row = 0; row < 4; row++) {
    
    
						//格子上方是空位置, 可以移动
						if (cells[row][col] != 0 && cells[row][col - 1] == 0) {
    
    
							return true;
						}
						//格子上方相邻的相等,可以移动
						if (cells[row][col] != 0 && (cells[row][col] == cells[row][col - 1])) {
    
    
							return true;
						}
					}
				}
				return false;
			}

			function canMoveRight() {
    
    
				for (var col = 0; col < 3; col++) {
    
    
					for (var row = 0; row < 4; row++) {
    
    
						//格子上方是空位置, 可以移动
						if (cells[row][col] != 0 && cells[row][col + 1] == 0) {
    
    
							return true;
						}
						//格子上方相邻的相等,可以移动
						if (cells[row][col] != 0 && (cells[row][col] == cells[row][col + 1])) {
    
    
							return true;
						}
					}
				}
				return false;
			}


			function test() {
    
    
				rightAction();
				state = CELL_MOVEING;
				animation.start(function() {
    
    
					//console.log("update");
					randomNumber();
					updateView();
					state = PLAYING;
				});
			}

			//更新显示,将表格中的数据,更新到界面显示
			function updateView() {
    
    
				for (var row = 0; row < 4; row++) {
    
    
					for (var col = 0; col < 4; col++) {
    
    
						var n = cells[row][col];
						var cell = $("cell" + row + col);
						//清楚显示的数据和显示样式
						cell.className = "cell";
						cell.innerHTML = "";
						if (n > 0) {
    
    
							//更新显示样式
							cell.className = "cell num" + n;
							//更新显示的数字
							cell.innerHTML = n;
						}
					}
				}

				$("score").innerHTML = score;
				$("finalScore").innerHTML = score;

			}
			//检查当前的表格中是否是满的,如果满了返回true,否则返回false
			function full() {
    
    
				for (var row = 0; row < 4; row++) {
    
    
					for (var col = 0; col < 4; col++) {
    
    
						if (cells[row][col] == 0) {
    
    
							return false;
						}
					}
				}
				return true;
			}
			//向表格随机插入一个数字,如果插入成功返回true,插入失败返回false
			function randomNumber() {
    
    
				if (full()) {
    
    
					return false;
				}
				while (true) {
    
    
					var col = parseInt(Math.random() * 4);
					var row = parseInt(Math.random() * 4);
					if (cells[row][col] == 0) {
    
    
						var n = Math.random() < 0.5 ? 2 : 4;
						cells[row][col] = n;
						return true;
					}
				}
			}

			function startAction() {
    
    
				$("gameOver").style.display = "none";
				for (var row = 0; row < 4; row++) {
    
    
					for (var col = 0; col < 4; col++) {
    
    
						cells[row][col] = 0;
					}
				}
				score = 0;
				randomNumber();
				randomNumber();
				updateView();
				state = PLAYING;
			}
			//元素查询方法
			function $(id) {
    
    
				return document.getElementById(id);
			}

			function has8192() {
    
    
				for (var row = 0; row < 4; row++) {
    
    
					for (var col = 0; col < 4; col++) {
    
    
						if (cells[row][col] == 8192) {
    
    
							return true;
						}
					}
				}
			}

			function hasSpace() {
    
    
				for (var row = 0; row < 4; row++) {
    
    
					for (var col = 0; col < 4; col++) {
    
    
						if (cells[row][col] == 0) {
    
    
							return true;
						}
					}
				}
			}


			/* 显示游戏结束界面 */
			function gameOver() {
    
    
				//发现8192游戏结束
				if (has8192()) {
    
    
					state = GAME_OVER;
					$("gameOver").style.display = "block";
					return true;
				}

				//发现空位置,游戏不结束    	
				if (hasSpace()) {
    
    
					return false;
				}

				//能够移动游戏不结束
				if (canMoveUp() || canMoveDown() || canMoveLeft() || canMoveRight()) {
    
    
					return false;
				}
				state = GAME_OVER;
				$("gameOver").style.display = "block";
				return true;
			}

			//软件启动初始代码
			window.onload = function() {
    
    
				$("newGame").onclick = function() {
    
    
					if (state == PLAYING)
						startAction();
				}
				$("restart").onclick = function() {
    
    
					if (state == GAME_OVER)
						startAction();
				}
				startAction();
				//监听键盘事件
				document.onkeydown = function(event) {
    
    
					if (state != PLAYING) {
    
    
						return;
					}
					var move = false;
					switch (event.keyCode) {
    
    
						case 37: //left
							move = leftAction();
							break;
						case 38: //up
							move = upAction();
							break;
						case 39: //right
							move = rightAction();
							break;
						case 40: //down
							move = downAction();
							break;
					}
					if (!move) {
    
    
						return;
					}
					if (effect) {
    
    
						state = CELL_MOVEING;
						animation.start(function() {
    
    
							//console.log("update");
							updateView();
							state = PLAYING;
							if (!gameOver()) {
    
    
								setTimeout(function() {
    
    
									randomNumber();
									updateView();
								}, 100);
							}
						});
					} else {
    
    
						if (!gameOver()) {
    
    
							setTimeout(function() {
    
    
								randomNumber();
								updateView();
							}, 100);
						}

						updateView();
						state = PLAYING;
					}
					gameOver();
				}; // 5211 0842 8479 快捷快递
			}
			
			// animation.js的部分
			var animation = {
    
    
				tasks: [], //动画任务数据
				timer: null, //动画定时器
				times: 10, //定时器次数
				interval: 1000 / 60, //定时器间隔
				add: function(obj, top, left) {
    
    
			
					//动画已经开始了,不能添加任务了!
					if (this.timer) {
    
    
						return false;
					}
					if (!obj) {
    
    
						return false;
					}
			
					//计算移动步伐
					var t = (top - obj.offsetTop) / this.times;
					var l = (left - obj.offsetLeft) / this.times;
			
					//添加动画任务,其中obj是被移动到对象,top, left是移动以后的位置
					var task = {
    
    
						topStep: t,
						leftStep: l,
						element: obj,
						step: function() {
    
    
							var t = this.element.offsetTop;
							this.element.style.top = (t + this.topStep) + "px";
							var l = this.element.offsetLeft;
							this.element.style.left = (l + this.leftStep) + "px";
						},
						clear: function() {
    
    
							this.element.style.top = "";
							this.element.style.left = "";
						}
					};
					this.tasks[this.tasks.length] = task;
					return true;
				},
				start: function(callback) {
    
    
					//如果定时器已经启动,就不能再启动定时器了
					if (this.timer) {
    
    
						return false;
					}
			
					console.log("STATRING");
					if (this.tasks.length == 0) {
    
    
						if (callback) {
    
    
							callback();
						}
						return false;
					}
			
					//如果有callback就交给this.callback
					if (callback) {
    
    
						this.callback = callback;
					}
					this.timer = setInterval(function() {
    
    
						//console.log("timeOut");
						//console.log(animation.times);
			
						for (var i = 0; i < animation.tasks.length; i++) {
    
    
							var task = animation.tasks[i];
							task.step();
						}
			
						animation.times--;
						if (animation.times < 0) {
    
    
							animation.stop();
						}
					}, this.interval);
					return true;
				},
				stop: function() {
    
    
					if (this.timer) {
    
    
						window.clearInterval(this.timer);
						this.timer = null;
						this.times = 10;
			
					}
			
					//结束以后执行callback()
					if (this.callback) {
    
    
						this.callback();
					}
					for (var i = 0; i < this.tasks.length; i++) {
    
    
						var task = this.tasks[i];
						task.clear();
					}
					this.tasks = []; //清空动画任务
				},
				callback: null //动画结束时候执行的方法
			};
			
		</script>
	</head>
	<body>
		<header>
			<h1>2048</h1>
		</header>
		<div id="gamePanel">
			<p><a class="button" id="newGame">New Game</a> Score: <span id="score">0</span><span class="writer">小灰灰版</span></p>
			<div id="gridPanel">
				<div class="grid"> </div>
				<div class="grid"> </div>
				<div class="grid"> </div>
				<div class="grid"> </div>

				<div class="grid"> </div>
				<div class="grid"> </div>
				<div class="grid"> </div>
				<div class="grid"> </div>

				<div class="grid"> </div>
				<div class="grid"> </div>
				<div class="grid"> </div>
				<div class="grid"> </div>

				<div class="grid"> </div>
				<div class="grid"> </div>
				<div class="grid"> </div>
				<div class="grid"> </div>

				<div class="cell " id="cell00"> </div>
				<div class="cell " id="cell01"> </div>
				<div class="cell " id="cell02"> </div>
				<div class="cell " id="cell03"> </div>

				<div class="cell " id="cell10"> </div>
				<div class="cell " id="cell11"> </div>
				<div class="cell " id="cell12"> </div>
				<div class="cell " id="cell13"> </div>

				<div class="cell " id="cell20"> </div>
				<div class="cell " id="cell21"> </div>
				<div class="cell " id="cell22"> </div>
				<div class="cell " id="cell23"> </div>

				<div class="cell " id="cell30"> </div>
				<div class="cell " id="cell31"> </div>
				<div class="cell " id="cell32"> </div>
				<div class="cell " id="cell33"> </div>

			</div>
			<div id="gameOver">
				<div>
					<!--背景 -->
				</div>
				<p>
					<!-- 前景 -->
					Game Over!<br>
					Score: <span id="finalScore">0</span><br>
					<a class="button" id="restart">Try Again!</a>
				</p>
			</div>
		</div>
	</body>
</html>

Guess you like

Origin blog.csdn.net/lishihuijava/article/details/107279551