index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>2048game</title>
<!-- 未使用虚拟地址时 -->
<!-- <link rel="stylesheet" href="./css/reset.css">
<link rel="stylesheet" href="./css/index.css">
<link href="favicon.ico" rel="shortcut icon">
<script src="./js/index.js"></script>
<script src="./js/basis.js"></script>
<script src="./js/animation.js"></script> -->
<!-- 使用虚拟地址时 -->
<link rel="stylesheet" href="static/css/reset.css">
<link rel="stylesheet" href="/static/css/index.css">
<link href="favicon.ico" rel="shortcut icon">
<script src="/static/js/index.js"></script>
<script src="/static/js/basis.js"></script>
<script src="/static/js/animation.js"></script>
</head>
<body>
<div class="wrapper">
<div id="score">
score
<br>
<span>0</span>
</div>
<a class="newGame" href="/static/index.html">New Game</a>
<!--<a class="newGame" href="index.html">New Game</a> -->
<div class="bottomBox">
<!-- 创建16个box的快捷方法
.box$*16 -->
<div class="box"><span></span></div>
<div class="box"><span></span></div>
<div class="box"><span></span></div>
<div class="box"><span></span></div>
<div class="box"><span></span></div>
<div class="box"><span></span></div>
<div class="box"><span></span></div>
<div class="box"><span></span></div>
<div class="box"><span></span></div>
<div class="box"><span></span></div>
<div class="box"><span></span></div>
<div class="box"><span></span></div>
<div class="box"><span></span></div>
<div class="box"><span></span></div>
<div class="box"><span></span></div>
<div class="box"><span></span></div>
</div>
</div>
<div class="gameOver">游戏结束<a href="/static/index.html">重新开始游戏</a></div>
<!-- <div class="gameOver">游戏结束<a href="index.html">重新开始游戏</a></div> -->
</body>
</html>
index.js
var board=new Array();
for(let i=0;i<4;i++){
board[i]=new Array(4).fill(0);
}
var score=0;
window.onload = function(){
let boxs=document.getElementsByClassName("box");
randomNumber2or4(board,boxs);
randomNumber2or4(board,boxs);
document.onkeydown=function(event){
event = event ||window.event;
switch(event.key){
case 'ArrowUp':
moveUp(board,boxs)
break;
case 'ArrowDown':
moveDown(board,boxs)
break;
case 'ArrowLeft':
moveLeft(board,boxs)
break;
case 'ArrowRight':
moveRight(board,boxs)
break;
default:
alert('按错键,应使用键盘上下左右键')
break;
}
document.getElementById("score").children[1].innerHTML=score;
}
}
basis.js
function randomNumber2or4(board,boxs){
while(isHasBlank(board)){
let randomi=Math.floor(Math.random()*4);
let randomj=Math.floor(Math.random()*4);
if(board[randomi][randomj]==0){
board[randomi][randomj]=Math.floor(1.05+Math.random())*2;
let index=4*randomi+randomj;
boxs[index].children[0].innerHTML=board[randomi][randomj];
numberColor(boxs[index].children[0]);
boxs[index].children[0].style.animation = "size0to1 1s";
break;
}
}
return true;
}
function isHasBlank(board){
for(let i=0;i<4;i++){
for(let j=0;j<4;j++){
if(board[i][j]==0){
return true;
}
}
}
return false;
}
function updateView(board,boxs,mergeIndex){
let unqualNum=0;
for(let i=0;i<4;i++){
for(let j=0;j<4;j++){
let index=4*i+j;
boxs[index].children[0].style.animation = "";
if(boxs[index].children[0].innerHTML!=board[i][j]){
boxs[index].children[0].innerHTML=board[i][j];
numberColor(boxs[index].children[0]);
unqualNum++;
}
}
}
for(let i=0;i<mergeIndex.length;i++){
boxs[4*mergeIndex[i][0]+mergeIndex[i][1]].children[0].style.animation = "sizeMergeTo1 1s";
}
if(unqualNum!=0){
randomNumber2or4(board,boxs);
}
gameResult(board);
return;
}
function moveUp(board,boxs){
let mergeIndex=[];
for(let j=0;j<4;j++){
let arr=[board[0][j],board[1][j],board[2][j],board[3][j]]
let temp=arrayNew(arr);
let mergeArr=temp[1];
for(let i=0;i<4;i++){
board[i][j]=temp[0][i];
for(let k=0;k<mergeArr.length;k++){
if(mergeArr[k]==i){
mergeIndex.push([i,j])
}
}
}
}
updateView(board,boxs,mergeIndex)
}
function moveDown(board,boxs){
let mergeIndex=[];
for(let j=0;j<4;j++){
let arr=[board[3][j],board[2][j],board[1][j],board[0][j]]
let temp=arrayNew(arr);
let mergeArr=temp[1];
for(let i=0;i<4;i++){
board[i][j]=temp[0][3-i];
for(let k=0;k<mergeArr.length;k++){
if(mergeArr[k]==(3-i)){
mergeIndex.push([i,j])
}
}
}
}
updateView(board,boxs,mergeIndex)
}
function moveLeft(board,boxs){
let mergeIndex=[];
for(let i=0;i<4;i++){
let arr=board[i]
let temp=arrayNew(arr);
let mergeArr=temp[1];
for(let j=0;j<4;j++){
board[i][j]=temp[0][j];
for(let k=0;k<mergeArr.length;k++){
if(mergeArr[k]==j){
mergeIndex.push([i,j])
}
}
}
}
updateView(board,boxs,mergeIndex)
}
function moveRight(board,boxs){
let mergeIndex=[];
for(let i=0;i<4;i++){
let arr=[board[i][3],board[i][2],board[i][1],board[i][0]];
let temp=arrayNew(arr);
let mergeArr=temp[1];
for(let j=0;j<4;j++){
board[i][j]=temp[0][3-j];
for(let k=0;k<mergeArr.length;k++){
if(mergeArr[k]==(3-j)){
mergeIndex.push([i,j])
}
}
}
}
updateView(board,boxs,mergeIndex)
}
function arrayNew(arr){
let newArray=[];
let mergeArr=[];
for(let i=0;i<arr.length;i++){
if(arr[i]!=0){
newArray.push(arr[i]);
}
}
for(let i=0;i<newArray.length-1;i++){
if(newArray[i]!=0){
if(newArray[i]==newArray[i+1]){
score+=newArray[i]*2;
mergeArr.push(i)
newArray[i]=newArray[i]*2;
newArray.splice(i+1, 1);
}
}
}
let temp=arr.length-newArray.length;
for(let i=0;i<temp;i++){
newArray.push(0);
}
return [newArray,mergeArr];
}
function numberColor(num){
let numText=num.innerHTML;
if(numText!=0){
num.style.display="block";
if(numText==2||numText==4){
num.style.color="#776e65";
}else{
num.style.color="#F9F6F2";
}
if(numText==2){
num.style.backgroundColor='#EEE4DA';
}else if(numText==4){
num.style.backgroundColor="#ede0c8";
}else if(numText==8){
num.style.backgroundColor="#f2b179";
}
else if(numText==16){
num.style.backgroundColor="#f59563";
}
else if(numText==32){
num.style.backgroundColor="#f67e60";
}
else if(numText==64){
num.style.backgroundColor="#f65e3b";
}
else if(numText==128){
num.style.backgroundColor="#edcf72";
}
else if(numText==256){
num.style.backgroundColor="#eccb60";
}
else if(numText==512){
num.style.backgroundColor="#ecc84e";
}
else if(numText==1024){
num.style.backgroundColor="#edc63a";
}
else if(numText==2048){
num.style.backgroundColor="#ebc32d";
}else{
num.style.backgroundColor="";
}
}else{
num.style.display="none";
}
}
function gameResult(board){
if(!isHasBlank(board)){
for(let i=0;i<4;i++){
for(let j=0;j<4;j++){
if(board[i][j]==0||(j+1<4&&board[i][j]==board[i][j+1])||(j-1>=0&&board[i][j]==board[i][j-1])||(i+1<4&&board[i][j]==board[i+1][j])||(i-1>=0&&board[i][j]==board[i-1][j])){
return;
}
}
}
let gameOver=document.getElementsByClassName("gameOver")[0];
gameOver.style.display='block';
}
}
index.less
body{
background-color: #faf8ef;
.wrapper{
width: 450px;
margin: 100px auto;
position: relative;
#score{
width: 100px;
height: 40px;
line-height: 20px;
text-align: center;
background-color: #bbada0;
border: solid 2px #bbada0;
border-radius: 2px;
color:#f9f8e9;
font-size: 16px;
font-weight:bold;
position: absolute;
left: 0px;
top:-70px;
span{
color: white;
font-size: 20px;
}
}
.newGame{
width: 110px;
height: 40px;
line-height: 40px;
text-align: center;
text-decoration: none;
background-color: #8F7A66;
border: solid 2px #8F7A66;
border-radius: 2px;
color:#f9f8e9;
font-size: 16px;
font-weight:bold;
position: absolute;
left: 340px;
top:-70px;
}
.bottomBox{
width: 440px;
height: 440px;
background-color: #BBADA0;
border: solid 5px #BBADA0;
border-radius: 5px;
.box{
width: 100px;
height: 100px;
background-color: #cdc1b4;
margin: 5px;
border-radius: 2px;
float:left;
span{
// span是内联元素,不设置display:block即使设置宽高也不能显示设置的宽高,只会根据其中内容来宽高
// display:none;
height: 100px;
line-height: 100px;
text-align: center;
font-size: 50px;
font-weight:bold;
color: #776e65;
}
}
}
}
.gameOver{
display: none;
position: absolute;
width: 100%;
height: 100%;
line-height: 755px;
background-color: rgb(202, 202, 230);
font-size: 50px;
text-align: center;
text-decoration: none;
opacity:0.7;
color: black;
a{
display: inline-block;
margin: 0 100px;
opacity:1;
color: green;
font-size: 50px;
// justify-content: center;
// width: 70px;
// height: 70px;
// background-color: antiquewhite;
}
}
}
@keyframes size0to1{
from{
transform: scale(0);
}
to{
transform: scale(1);
}
}
@keyframes sizeMergeTo1{
from{
transform: scale(1.3);
}
to{
transform: scale(1);
}
}
server.js
const express = require('express');
const app = express();
const expressPort=3000;
app.listen(expressPort,() => {
console.log("running……on" + expressPort);
})
app.use('/static',express.static('public'));
app.get('/',(req,res,next)=>{
res.sendFile(__dirname +'/public/index.html')
})
初步记录:使用js编写2048代码,并通过express实现页面展示,可能有些小问题。 难点1:在初始随机产生空白和在有数字移动后随机产生一个空白 利用数组中的数据判断是否有空白,随机在空白索引处产生随机数字2或4,然后更新dom显示。
难点2:实现根据键盘实现上下左右移动并把相同数字合并 第一次是直接操作dom元素,根据不同方向依次移动元素。 第二次利用数组,对数组的数值操作后,再更新dom元素的文本值和颜色。 **注:**这里当时出现一个小bug,即当在某个方向没有可移动和可移动的元素时,在用户依然点击这个方向,页面应该无反应,所以在产生随机空白时应该加上限制:即在移动后更新所有位置元素,若发现无一个位置变化则不反应,若有变化则随机在空白处产生一个数字2/4。
难点3: 出新数字的动画和相同数字合并时的动画 在出现新产生的数字时,在其更新显示颜色时,添加并设置动画属性。 在对数组数据进行移动和合并操作时,给经过合并后的数字添加标志,然后在更新视图时,对有合并标志位置的div添加动画属性。
难点4:游戏结束后的弹窗-整个页面中其他的都点不动 使用和页面一样大小的div,在游戏结束前隐藏,游戏结束时显示。 至于游戏的结束标志:16个格子被铺满,且没有地方可以合并时为结束。
接下来计划:看看原版2048如何实现的
|