思想
简述项目:
1.功能:该项目完成的功能就是通过上下左右键,使相同的方块进行合并,相同的方块合并时,会将方块上面的值变为原来的二倍,游戏的规则是,通过键盘事件,合成最大的数字
2.如何完成:
初始化:
(1)首先进行面板的初始化,根据设置的行数,列数,方块的宽高,以及边框的宽度,计算之后,设置面板的宽高
(2)根据行数和列数,创建值为0的小方块,将他们添加到面板里。
(3)初始化装各个位置的值的二维数组valueMap,以及装生成的方块的squareSet,随机生成两个方块,(调用生成值为2或者4的函数,随机生成行数,列数,生成方块)
(4)进行鼠标事件的绑定,当键盘点击,触发移动函数,
触发移动函数:
在移动函数里面先判断是否游戏结束(判断依据是遍历装方块的数组的值,看是否有为null的,或者看是否有相邻方块值相等存在),如果游戏未结束,调用分析函数(将boardSet存在的值闯到一个数组里面),如果相邻的值相等的话,将后一个值放在前一个值的nextBoard属性里,返回合并后的数组,重新渲染页面,将boardSet初始化,valueMap初始化,遍历原来的boardSet,如果有nextBoard的话,创建一个值为当前值的二倍的方块,添加到面板里,并将值添加到valueMap里面。
init 初始化函数
功能:
1.初始化面板initBoard,
2.初始化二维数组boardSet(创建值为0的方块,在各个位置,调用createSquare),valueMap[i][j] = 0,squareSet[i][j] = null,将boardSet添加到my2048里
3.随机生成两个方块randGenerateSquare,
4.键盘事件绑定,触发move,根据e.key的不同,传给move的方向也不同,设置锁,如果当前键盘事件还未响应完,不对新的键盘事件进行相应。
initBoard 初始化面板
功能:根据设置的rows,cols,squareWidth,spacing的值计算并设置my2048的宽和高
createSquare 生成方块
根据传入的值,left,top,cols,rows创建方块,并将num,cols,rows挂在创建的temp上,并将temp返回。
randGenerateSquare 随机生成方块
1.随机生成行和列,调用randSquareNum生成方块的值(随机生成2或者4)
2.根据行,列,值生成方块,将他添加到my2048里,将他加入squareSet,值加入valueMap
randSquareNum 随机生成2或4,并返回
move 根据按键进行重新渲染
1.调用isOver,判断是否结束
2.创建一个新的newSquareSet,通过调analysisActions(direction),获取合并之后的数组
3.设置定时器,每300ms,调用refresh(newSquareSet)
4.如果isChange = true ,调用randGenerateSquare,调用结束, lock = true; isChange = false;
refresh(newSquareSet) 修改squareSet,valueMap
1.将squareSet清空,创建一个都是null的二维数组newValueMap
2.遍历newSquareSet,如果有nextSquare属性的话,重新创建值为当前值二倍的方块,并将方块渲染到my2048,将原来的方块,以及原来方块的nextSquare在my2048删除,没有nextSquare属性的话,创建一个新的方块,值为当前值,添加到squareSet,渲染到my2048,删除原来的div
3.遍历newSquareSet的时候,如果valueMap的值与squareSet的值不相同的话,isChange = true,newValueMap = squareSet的值如果某个位置元素不存在的话,newValueMap填0
4.最后将newValueMap 给 valueMap
analysisiAction 分析键盘事件触发之后,方块的新位置
1.调用generateNullMap创建一个空的新的NewSquareSet
2.根据direction的不同进行大同小异的处理,以direction = directionEnum.left为例,遍历squareSet数组,将值不为null的,push到temp
3.调用getNewLocation,并且将temp传进去,用temp接收返回值,获取到合并之后的数组
4.将temp里面的值添加到NewSquareSet
5.添加动画,使元素移动
getNewLocation(arr) 将相邻位置值相同的div添加到上一个的nextSquare属性上
1.将arr[0] 放到temp里面,for循环,i=1 -> arr.length - 1
2.判断temp[temp.length - 1]是否有nextSquare,如果没有的话,比较a[i]与temp[temp.length - 1]的值是否相等,如果相等的话,将a[i]放到temp[temp.length - 1]的nextSquare里,如果两个值不等,将他添加到temp里面
3.返回temp
==generateNullMap ==
1.创建一个值都为null的二维数组,并返回
isOver
判断squareSet[i][j]是否还有null,如果有,返回false,不结束
不满足上面的判断,接下来判断相邻的值是否相等(squareSet[i][j] 与 squareSet[i][j + 1]的值,或者squareSet[i + 1][j] 与 squareSet[i][j ]),相等则不结束
否则结束
var my2048;
var rows = 4;
var cols = 4;
var squareWidth = 100;
var spacing = 12;
var boardSet = [];
var squareSet = [];
var valueMap = [];
var colorMapping = {"0": "#ccc0b3", "2": "#eee4da", "4": "#ede0c8", "8": "#f2b179", "16": "#f59563", "32": "#f67e5f", "64": "#f65e3b", "128": "#edcf72", "256" : "#edcc61", "512": "#9c0", "1024": "#33b5e5", "2048": "#09c"};
var directionEnum = {left:{x:-1, y:0, key:"left"}, right:{x:1, y:0, key:"left"}, top:{x:0, y:-1, key:"top"}, down:{x:0, y:1, key:"top"}};
var lock = true;
var isChange = false;
function move(direction) {
if (isOver()) {
alert("game over ~!");
return;
}
var newSquareSet = analysisActions(direction);
//收尾(保证最终一致性)
setTimeout(function () {
refresh(newSquareSet);
if (isChange) {
randGenerateSquare();
}
lock = true;
isChange = false;
}, 300);
}
function analysisActions(direction) {
var newSquareSet = generateNullMap();
if (direction == directionEnum.left) {//向左
console.log("向左");
for (var i = 0 ; i < squareSet.length ; i ++) {
var temp = [];
for (var j = 0 ; j < squareSet[i].length ; j ++) {
if (squareSet[i][j] != null) {
temp.push(squareSet[i][j]);
}
}
temp = getNewLocation(temp);
for (var k = 0 ; k < newSquareSet[i].length ; k ++) {
if (temp[k]) {
newSquareSet[i][k] = temp[k];
}
}
}
} else if (direction == directionEnum.right) {//向右
console.log("向右");
for (var i = 0 ; i < squareSet.length ; i ++) {
var temp = [];
for (var j = squareSet[i].length - 1 ; j >= 0 ; j --) {
if (squareSet[i][j] != null) {
temp.push(squareSet[i][j]);
}
}
temp = getNewLocation(temp);
for (var k = newSquareSet[i].length - 1 ; k >= 0 ; k --) {
if (temp[newSquareSet[i].length - 1 - k]) {
newSquareSet[i][k] = temp[newSquareSet[i].length - 1 - k];
}
}
}
} else if (direction == directionEnum.top) {//向前
console.log("向前");
for (var j = 0 ; j < squareSet[0].length ; j ++) {
var temp = [];
for (var i = 0 ; i < squareSet.length ; i ++) {
if (squareSet[i][j] != null) {
temp.push(squareSet[i][j]);
}
}
temp = getNewLocation(temp);
for (var k = 0 ; k < newSquareSet.length ; k ++) {
if (temp[k]) {
newSquareSet[k][j] = temp[k];
}
}
}
} else {//向后
console.log("向后");
for (var j = 0 ; j < squareSet[0].length ; j ++) {
var temp = [];
for (var i = squareSet.length - 1 ; i >= 0 ; i --) {
if (squareSet[i][j] != null) {
temp.push(squareSet[i][j]);
}
}
temp = getNewLocation(temp);
for (var k = newSquareSet.length - 1 ; k >= 0 ; k --) {
if (temp[newSquareSet.length - 1 - k]) {
newSquareSet[k][j] = temp[newSquareSet.length - 1 - k];
}
}
}
}
//动画
for (var i = 0 ; i < newSquareSet.length ; i ++) {
for (var j = 0 ; j < newSquareSet[i].length ; j ++) {
if (newSquareSet[i][j] == null) {
continue;
}
newSquareSet[i][j].style.transition = direction.key + " 0.3s";
newSquareSet[i][j].style.left = (j + 1) * spacing + j * squareWidth + "px";
newSquareSet[i][j].style.top = (i + 1) * spacing + i * squareWidth + "px";
if(newSquareSet[i][j].nextSquare) {
newSquareSet[i][j].nextSquare.style.transition = direction.key + " 0.3s";
newSquareSet[i][j].nextSquare.style.left = (j + 1) * spacing + j * squareWidth + "px";
newSquareSet[i][j].nextSquare.style.top = (i + 1) * spacing + i * squareWidth + "px";
}
}
}
return newSquareSet;
}
function getNewLocation(arr) {
if (arr.length == 0) {
return [];
}
var temp = [];
temp.push(arr[0]);
for (var i = 1 ; i < arr.length ; i ++) {
if (arr[i].num == temp[temp.length - 1].num && (!temp[temp.length - 1].nextSquare || temp[temp.length - 1].nextSquare == null)) {
temp[temp.length - 1].nextSquare = arr[i];
} else {
temp.push(arr[i]);
}
}
return temp;
}
function generateNullMap () {
var newValueMap = [];
for (var i = 0 ; i < rows ; i ++) {
newValueMap[i] = [];
for (var j = 0 ; j < cols ; j ++) {
newValueMap[i][j] = null;
}
}
return newValueMap;
}
function isOver() {
for (var i = 0 ; i < squareSet.length ; i ++) {
for (var j = 0 ; j < squareSet[i].length ; j ++) {
if (squareSet[i][j] == null) {
return false;
}
if (squareSet[i][j + 1] && squareSet[i][j].num == squareSet[i][j + 1].num || squareSet[i + 1] && squareSet[i + 1][j] && squareSet[i][j].num == squareSet[i + 1][j].num){
return false;
}
}
}
return true;
}
function refresh(newSquareSet) {//纠正位图,保证最终一致性
squareSet = generateNullMap();
var newValueMap = generateNullMap();
for (var i = 0 ; i < rows ; i ++) {
for (var j = 0 ; j < cols ; j ++) {
//新的存在则添加
if (newSquareSet[i][j]) {
if (newSquareSet[i][j].nextSquare) {
var temp = createSquare(newSquareSet[i][j].num * 2, newSquareSet[i][j].offsetLeft, newSquareSet[i][j].offsetTop, i, j);
squareSet[i][j] = temp;
my2048.append(temp);
my2048.removeChild(newSquareSet[i][j].nextSquare);
my2048.removeChild(newSquareSet[i][j]);
} else {
var temp = createSquare(newSquareSet[i][j].num, newSquareSet[i][j].offsetLeft, newSquareSet[i][j].offsetTop, i, j);
squareSet[i][j] = temp;
my2048.append(temp);
my2048.removeChild(newSquareSet[i][j]);
}
if (valueMap[i][j] != squareSet[i][j].num) {
isChange = true;
}
newValueMap[i][j] = squareSet[i][j].num;
} else {
newValueMap[i][j] = 0;
}
}
}
valueMap = newValueMap;
}
function randSquareNum() {
return Math.random() >= 0.5 ? 4 : 2;
}
function randGenerateSquare() {
for (;;) {
var randRow = Math.floor(Math.random() * rows);
var randCol = Math.floor(Math.random() * cols);
if (valueMap[randRow][randCol] == 0) {
var temp = createSquare(randSquareNum(), randCol * squareWidth + (randCol + 1) * spacing, randRow * squareWidth + (randRow + 1) * spacing, randRow, randCol);
valueMap[temp.row][temp.col] = temp.num;
squareSet[temp.row][temp.col] = temp;
my2048.appendChild(temp);
return true;
}
}
}
function createSquare(value, left, top, row, col) {
var temp = document.createElement("div");
temp.style.width = squareWidth + "px";
temp.style.height = squareWidth + "px";
temp.style.left = left + "px";
temp.style.top = top + "px";
temp.style.background = colorMapping[value];
temp.style.lineHeight = squareWidth + "px";
temp.style.textAlign = "center";
temp.style.fontSize = 0.4 * squareWidth + "px";
temp.num = value;
temp.row = row;
temp.col = col;
if (value > 0) {
temp.innerHTML = "" + value;
}
return temp;
}
function initBoard() {
my2048 = document.getElementById("my2048");
my2048.style.width = cols * squareWidth + (cols + 1) * spacing + "px";
my2048.style.height = rows * squareWidth + (rows + 1) * spacing + "px";
}
function init() {
//初始化棋盘
initBoard();
for (var i = 0 ; i < rows ; i ++){
boardSet[i] = [];
valueMap[i] = [];
squareSet[i] = [];
for (var j = 0 ; j < cols ; j ++){
valueMap[i][j] = 0;
squareSet[i][j] = null;
boardSet[i][j] = createSquare(0, j * squareWidth + (j + 1) * spacing, i * squareWidth + (i + 1) * spacing, i, j);
my2048.appendChild(boardSet[i][j]);
}
}
//初始化方块
randGenerateSquare();
randGenerateSquare();
//添加事件
document.addEventListener("keydown", function(e) {
if (!lock) return;
lock = false;
switch (e.key) {
case "ArrowUp": move(directionEnum.top);break;
case "ArrowDown": move(directionEnum.down);break;
case "ArrowLeft": move(directionEnum.left);break;
case "ArrowRight": move(directionEnum.right);break;
default : {
lock = true;
}
}
})
}
window.onload = function () {
init();
}
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="index.js"></script>
<link href="index.css" rel="stylesheet"/>
</head>
<body>
<div id="my2048">
</div>
</body>
</html>
css
body, div, span, a {
margin: 0;
padding: 0;
}
#my2048 {
margin-left: auto;
margin-right: auto;
position: relative;
background: #bbada0;
}
#my2048 div {
position: absolute;
display: inline-block;
border: 0px;
}