一、界面展示及簡單介紹
先上完成後不同尺寸螢幕下的效果圖,界面模仿的原版2048:
(768px<螢幕)
(500px<螢幕<768px)
(小于500px的超小螢幕)
原來預想左邊是遊戲清單,右邊是排行榜,暫時沒有做。
整體使用了bootstrap的栅格布局,左右兩列在xs尺寸下隐藏,中間列獨占12格。
在螢幕小于500px的時候額外設定樣式:
@media screen and (max-width: 500px){
...
}
動畫效果的實作學習了慕課網的2048課程,也算學習了一種新思路。
其它效果:
·手機端的滑動操作
·頁面放大縮小格子跟随移動
二、主要實作函數
這裡就隻貼出主要的代碼,盡量增加注釋,讓大家更好地了解實作邏輯。完整項目代碼可以在文章末尾的github位址中clone。
1.移動的邏輯,以向左移動為例
//判斷是否可以移動 board為4*4對應格子的數組
function canMoveLeft(){
for (let i = 0; i<4; i++){
for (let 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 moveLeft() {
if(!canMoveLeft())//判斷是否可以左移
return;
for(let x = 0; x<4; x++){
//merged數組對應該行四個格子,判斷是否已經合并過,避免[2,2,2,2]合并為[8,0,0,0]之類情況
var merged = [false,false,false,false];
for(let y = 1; y<4; y++) {
if (board[x][y] !=0){//目前行從左至右尋找有數字點
var currentnum = board[x][y]; //目前移動點的數字
var last = y; //目前點移動或尋找合并的過程中記錄目前位置
for(var n = y-1; n>=0; n--){//從目前點左側相鄰格開始往左尋找空格或可合并格
if(board[x][n] == 0){
board[x][n] = currentnum;
board[x][last] = 0;
last = n;
continue;
}
else if(board[x][n] == currentnum && !merged[n]){
board[x][n] = currentnum * 2;
score+= currentnum * 2;
board[x][last] = 0;
last = n;
merged[n] = true;
continue;
}
else
break;//相鄰格不能移動和合并直接結束
}
moveAnimation(x,y,x,n+1,merged[n+1])//移動動畫
}
}
}
setTimeout("updateBoardView()",200) //移動完成後更新視圖
setTimeout("ranNumber()",200)//等待移動動畫完成後再生成新數字
}
2.動畫效果的實作
實作思路為原本在html中寫的16個div格子(grid-cell)隻用來實作視覺效果,實際并沒有對這16個格子進行操作,而是在每次根據二維數組board記錄的資料生成16個position=absolute的div(number-cell),number-cell不顯示時大小都為0,位置都在對應實grid-cell的中心,在生成數字或移動時使用animate方法對number-cell進行操作,動畫結束後再更新視圖,代碼如下:
function updateBoardView() { //更新顯示
$(".number-cell").remove();
for (let i =0; i < 4; i++)
for (let j = 0; j<4; j++){
$(".grid-container").append('<div class="number-cell" id="number-cell-'+i+'-'+j+'"></div>');
var theNumberCell = $('#number-cell-'+i+'-'+j);
if(board[i][j] == 0){
theNumberCell.css('width','0px');
theNumberCell.css('height','0px');
theNumberCell.css('top',getPosTop(i)+cellSize/2);
theNumberCell.css('left',getPosLeft(j)+cellSize/2);
}//cellSize為每個格子的長寬 cellSpace為每個格子之間的間距
else {
theNumberCell.css('width',cellSize);
theNumberCell.css('height',cellSize);
theNumberCell.css('top',getPosTop(i));
theNumberCell.css('left',getPosLeft(j));
theNumberCell.css('background-color',getCellColor(board[i][j]));
theNumberCell.css('color',getNumColor(board[i][j]));
theNumberCell.text(board[i][j]);
}
}
}
移動動畫代碼:
//傳入起始點坐标,終點坐标及是否是合并點
function moveAnimation(fromx,fromy,tox,toy,merged) { //移動動畫
var numberCell = $('#number-cell-'+fromx+'-'+fromy);
numberCell.animate({
top:getPosTop(tox),//根據行數确定top距離
left:getPosLeft(toy)//根據列數确定left距離
},100)
if (merged){ //合并動畫 脹大再還原的過程
numberCell.animate({
width:cellSize+cellSpace*2,
height:cellSize+cellSpace*2,
top:getPosTop(tox)-cellSpace,
left:getPosLeft(toy)-cellSpace
},50)
numberCell.animate({
width:cellSize,
height:cellSize,
top:getPosTop(tox),
left:getPosLeft(toy)
},50)
}
}
三、總結
整個遊戲在整體的邏輯實作上會花費比較久的時間,另一個難點就是動畫的實作,幸好慕課網的教程給出了一種解決思路,總體來說2048還是一個比較簡單的項目。
項目完整代碼github位址:https://github.com/GaoMinjian/2048
項目線上體驗位址一:https://gaominjian.github.io/2048/index.html
項目線上體驗位址二:http://112.74.53.108/2048/