前段时间有个很火的游戏叫2048,刚好趁着暑假想练习前端,顺带就把它作为练习项目做成Web Applications
该游戏基于HTML5+JS+CSS
文件结构:
index.html 游戏界面展示
index.css 主界面的CSS样式,16方格采用绝对布局的方式
main.js 游戏的主要逻辑
show.js 游戏的一些动画效果
support.js 底层支撑
jquery.js
页面做了响应式布局,游戏效果如下:
代码如下:
index.hml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta name="viewport" content="width=device-width,height=device-height,initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"/> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>New Game 2048</title> <script type="text/javascript" src="jquery-1.8.2.min.js"></script> <script type="text/javascript" src="show.js"></script> <script type="text/javascript" src="support.js"></script> <script type="text/javascript" src="main.js"></script> <link type="text/css" rel="stylesheet" href="index.css" /> </head> <body> <header> <h1>2048</h1> <a href="javascript:newGame();" id="newGameButton">New Game</a> <p>Score:<span id="score">0</span></p> </header> <div id="grid-container"> <div class="grid-cell" id="grid-cell-0-0"></div> <div class="grid-cell" id="grid-cell-0-1"></div> <div class="grid-cell" id="grid-cell-0-2"></div> <div class="grid-cell" id="grid-cell-0-3"></div> <div class="grid-cell" id="grid-cell-1-0"></div> <div class="grid-cell" id="grid-cell-1-1"></div> <div class="grid-cell" id="grid-cell-1-2"></div> <div class="grid-cell" id="grid-cell-1-3"></div> <div class="grid-cell" id="grid-cell-2-0"></div> <div class="grid-cell" id="grid-cell-2-1"></div> <div class="grid-cell" id="grid-cell-2-2"></div> <div class="grid-cell" id="grid-cell-2-3"></div> <div class="grid-cell" id="grid-cell-3-0"></div> <div class="grid-cell" id="grid-cell-3-1"></div> <div class="grid-cell" id="grid-cell-3-2"></div> <div class="grid-cell" id="grid-cell-3-3"></div> </div> </body> </html>
index.css
@charset "utf-8"; /* CSS Document */ header{ display:block; margin:0 auto; width:100%; text-align:center; } header h1{ font-family:Arial, Helvetica, sans-serif; font-size:40px; font-weight:bold; } header #newGameButton{ display:block; margin:20px auto; padding:10px 10px; width:100px; background-color:#930; font-family:Arial, Helvetica, sans-serif; color:#FFF; border-radius:10px; text-decoration:none; } header #newGameButton:hover{ background-color:#963; } header p{ font-family:Arial, Helvetica, sans-serif; font-size:25px; margin:10px auto; } #grid-container{ width:460px; height:460px; padding:20px; margin:10px auto; background-color:#930; border-radius:10px; position:relative; } .grid-cell{ width:100px; height:100px; background-color:#96F; border-radius:6px; position:absolute; } .number-cell{ font-family:Arial, Helvetica, sans-serif; border-radius:6px; font-size:60px; line-height:100px; font-weight:bold; text-align:center; position:absolute; }
main.js
// JavaScript Document var board=new Array(); var score=0; sx=0; sy=0; ex=0; ey=0; $(document).ready(function(e) { newGame(); }); function newGame(){ //初始化游戏 forMobile(); init(); //随机生成2个数 getOneNumber(); getOneNumber(); } function init(){ for(var i=0;i<4;i++){//遍历div for(var j=0;j<4;j++){ var gridCell=$("#grid-cell-"+i+"-"+j); gridCell.css("top",getTop(i,j)); gridCell.css("left",getLeft(i,j)); } } //board一维转二维 for(var i=0;i<4;i++){ board[i]=new Array(); for(var j=0;j<4;j++){ board[i][j]=0; } } updateBoardView(); score=0; } function forMobile(){ if( documentWidth > 500 ){ gridContainerWidth = 500; cellSpace = 20; cellSideLength = 100; } $('#grid-container').css('width',gridContainerWidth - 2*cellSpace); $('#grid-container').css('height',gridContainerWidth - 2*cellSpace); $('#grid-container').css('padding', cellSpace); $('#grid-container').css('border-radius',0.02*gridContainerWidth); $('.grid-cell').css('width',cellSideLength); $('.grid-cell').css('height',cellSideLength); $('.grid-cell').css('border-radius',0.02*cellSideLength); } function getOneNumber(){//noSpace没空间返回true if(noSpace(board)){ return false; }else{ // 随机一个位置 var x=parseInt(Math.floor(Math.random()*4)); var y=parseInt(Math.floor(Math.random()*4)); var flag=0; while(flag<40){ if(board[x][y]==0)break; var x=parseInt(Math.floor(Math.random()*4)); var y=parseInt(Math.floor(Math.random()*4)); flag++; } if(flag==40){ for(var i=0;i<4;i++){ for(var j=0;j<4;j++){ if(board[i][j]==0){ x=i; y=j; } } } } //随机生成一个数(2or4) var number=Math.random()<0.5?2:4; //在随机的位置显示生成的随机数 board[x][y]=number; showNumber(x,y,number); return true; } } function updateBoardView(){//显示数字 $(".number-cell").remove(); for(var i=0;i<4;i++){ for(var j=0;j<4;j++){ $("#grid-container").append('<div class="number-cell" id="number-cell-'+i+'-'+j+'"></div>'); var numberCell=$("#number-cell-"+i+"-"+j); //若数字为0则不显示 if(board[i][j]==0){ numberCell.css("width","0px"); numberCell.css("height","0px"); numberCell.css('top',getTop(i,j) + cellSideLength/2 ); numberCell.css('left',getLeft(i,j) + cellSideLength/2 ); }else{ numberCell.css('width',cellSideLength); numberCell.css('height',cellSideLength); numberCell.css("top",getTop(i,j)); numberCell.css("left",getLeft(i,j)); //填充div的背景颜色和前景颜色 numberCell.css("background-color",getNumberBackgroundColor(board[i][j])); numberCell.css("color",getNumberColor(board[i][j])); numberCell.text(board[i][j]); } } } $('.number-cell').css('line-height',cellSideLength+'px'); $('.number-cell').css('font-size',0.6*cellSideLength+'px'); } function isOver(){ if(noSpace(board)&&noMove(board)){ //alert(111); gameOver(); } } function gameOver(){ alert("游戏结束!"); } //监听键盘事件 $(document).keydown(function(event){ switch(event.keyCode){ case 37://left if(moveLeft()){ event.preventDefault(); setTimeout("getOneNumber()",200); isOver(); } break; case 38://up if(moveUp()){ event.preventDefault(); setTimeout("getOneNumber()",200); isOver(); } break; case 39://right if(moveRight()){ event.preventDefault(); setTimeout("getOneNumber()",200); isOver(); } break; case 40://down if(moveDown()){ //alert(1); event.preventDefault(); setTimeout("getOneNumber()",200); isOver(); } break; default: break; } }); document.addEventListener('touchstart',function(event){//回调函数,返回信息存在event sx=event.touches[0].pageX; sy=event.touches[0].pageY; }); document.addEventListener('touchend',function(event){ ex=event.changedTouches[0].pageX; ey=event.changedTouches[0].pageY; dx=ex-sx; dy=ey-sy; if(Math.abs(dx)<documentWidth*0.3&&Math.abs(dy)<documentWidth*0.3)return;// 防误触碰 if(Math.abs(dx)>=Math.abs(dy)){ //x if(dx>0){ //右 if(moveRight()){ setTimeout("getOneNumber()",200); isOver(); } }else{ //左 if(moveLeft()){ setTimeout("getOneNumber()",200); isOver(); } } }else{ //y if(dy>0){ //下 if(moveDown()){ setTimeout("getOneNumber()",200); isOver(); } }else{ //上 if(moveUp()){ setTimeout("getOneNumber()",200); isOver(); } } } }); document.addEventListener('touchmove',function(event){ event.preventDefault();//安卓4.0以上会有触碰bug }); function moveLeft(){ if(!canMoveLeft(board))return false; for(var i=0;i<4;i++){ for(var j=1;j<4;j++){ if(board[i][j]!=0){ for(var k=0;k<j;k++){ if(board[i][k]==0&&noBlockH(i,k,j,board)){ //move showMoveAnimation(i,j,i,k); board[i][k]=board[i][j]; board[i][j]=0; continue; }else if(board[i][k]==board[i][j]&&noBlockH(i,k,j,board)){ showMoveAnimation( i , j , i , k ); //叠加 board[i][k]+=board[i][j]; board[i][j]=0; score+=board[i][k]; updateScore(score); continue; } } } } } setTimeout("updateBoardView()",200); return true; } function moveRight(){ if(!canMoveRight(board))return false; for(var i=0;i<4;i++){ for(var j=2;j>=0;j--){//除最右列以外,遍历所有 for(var k=3;k>j;k--){ if(board[i][k]==0&&noBlockH(i,k,j,board)){ showMoveAnimation( i , j , i , k ); board[i][k]=board[i][j]; board[i][j]=0; continue; }else if(board[i][k]==board[i][j]&&noBlockH(i,k,j,board)){ showMoveAnimation( i , j , i , k ); board[i][k]+=board[i][j]; board[i][j]=0; score+=board[i][k]; updateScore(score); continue; } } } } setTimeout("updateBoardView()",200); return true; } function moveUp(){ if(!canMoveUp(board))return false; //能移动,怎么移 for(var j=0;j<4;j++){ for(var i=1;i<4;i++){ for(var k=0;k<i;k++){ if(board[k][j]==0&&noBlockV(j,k,i,board)){ showMoveAnimation( i , j , k , j ); board[k][j]=board[i][j]; board[i][j]=0; continue; }else if(board[k][j]==board[i][j]&&noBlockV(j,k,i,board)){ showMoveAnimation( i , j , k , j ); board[k][j]+=board[i][j]; board[i][j]=0 score+=board[i][k]; updateScore(score); continue; } } } } setTimeout("updateBoardView()",200); return true; } function moveDown(){ //先判断是否能移动 if(!canMoveDown(board))return false; for(var j=0;j<4;j++){ for(var i=2;i>=0;i--){ for(var k=3;k>i;k--){ if( board[k][j] == 0 && noBlockV( j , i , k , board ) ){ showMoveAnimation( i , j , k , j ); board[k][j] = board[i][j]; board[i][j] = 0; continue; }else if( board[k][j] == board[i][j] && noBlockV( j , i , k , board ) ){ showMoveAnimation( i , j , k , j ); board[k][j]+= board[i][j]; board[i][j] = 0; score+=board[i][k]; updateScore(score); continue; } } } } setTimeout("updateBoardView()",200); return true; }
show.js
// JavaScript Document function showNumber(x,y,number){ var numberCell=$("#number-cell-"+x+"-"+y); //填充文字的颜色 numberCell.css('background-color',getNumberBackgroundColor(number)); numberCell.css('color',getNumberColor(number)); numberCell.text(number); numberCell.animate({ width:cellSideLength+"px", height:cellSideLength+"px", top:getTop(x,y), left:getLeft(x,y) },60) } function showMoveAnimation(fromx,fromy,tox,toy){ var numberCell = $('#number-cell-' + fromx + '-' + fromy ); numberCell.animate({ top:getTop( tox , toy ), left:getLeft( tox , toy ) },200); } function updateScore(score){ $("#score").html(score+''); }
support.js
// JavaScript Document documentWidth = window.screen.availWidth; gridContainerWidth = 0.92 * documentWidth; cellSideLength = 0.18 * documentWidth; cellSpace = 0.04*documentWidth; function getTop(i,j){ return cellSpace + i*( cellSpace + cellSideLength ); } function getLeft(i,j){ return cellSpace + j*( cellSpace + cellSideLength ); } function getNumberBackgroundColor( number ){ switch( number ){ case 2:return "#eee4da";break; case 4:return "#ede0c8";break; case 8:return "#f2b179";break; case 16:return "#f59563";break; case 32:return "#f67c5f";break; case 64:return "#f65e3b";break; case 128:return "#edcf72";break; case 256:return "#edcc61";break; case 512:return "#9c0";break; case 1024:return "#33b5e5";break; case 2048:return "#09c";break; case 4096:return "#a6c";break; case 8192:return "#93c";break; } return "black"; } function getNumberColor( number ){ if( number <= 4 ) return "#776e65"; return "white"; } function noBlockH(row,k,j,board){//row行,从k列到j列 for(var i=k+1;i<j;i++){ if(board[row][i]!=0){ return false; } } return true; } function noBlockV(col,row1,row2,board){//j列,从k行到i行 for(var i=row1+1;i<row2;i++){ if(board[i][col]!=0){ return false; } } return true; } function canMoveLeft(board){ for(var i=0;i<4;i++){ for(var j=1;j<4;j++){ if(board[i][j]!=0){ if(board[i][j-1]==0||board[i][j-1]==board[i][j]){ return true; } } } } return false; } function canMoveRight(board){ for(var i=0;i<4;i++){ for(var j=2;j>=0;j--){ if(board[i][j]!=0){ if(board[i][j+1]==0||board[i][j]==board[i][j+1]){ return true; } } } } return false; } function canMoveUp(board){ for(var j=0;j<4;j++){ for(var i=1;i<4;i++){ if(board[i][j]!=0){ if(board[i-1][j]==0||board[i][j]==board[i-1][j]){ return true; } } } } return false; } function canMoveDown(board){ for(var j=0;j<4;j++){ for(var i=2;i>=0;i--){ if(board[i][j]!=0){ if(board[i+1][j]==0||board[i][j]==board[i+1][j]){ return true; } } } } return false; } function noMove(board){ if( canMoveLeft(board)|| canMoveRight(board)|| canMoveUp(board)||canMoveDown(board)){ return false; } alert(112); return true; } function noSpace(board){//没空间,是返回true,否返回false for( var i = 0 ; i < 4 ; i ++ ){ for( var j = 0 ; j < 4 ; j ++ ){ if(board[i][j]==0){ return false; } } } //循环终止于return 不能放在上面的if判断语句 return true; }