我的前端故事----歡樂大富翁( ̄︶ ̄)↗ (搖骰子+棋盤)


      十一馬上就要到了~~做為前端的小伙伴~活動頁面必然是少不了了~那怎么吸引眼球呢?當然是玩游戲啦~這次就帶來一個我為十一做的小游戲,作為活動頁中的襯托~~

      那我們要做個什么樣的游戲呢?先上圖~

4443

       由於實際的效果圖因為是公司的活動,我就不貼出來了~這里用這個棋盤來大致說明一下效果咯~下面是游戲規則:

       1,當用戶看到棋盤的時候,棋子出現在上一次停留下來的位置;

       2,當用戶點擊開始后,中間的骰子開始搖動,最后停止在服務器返回的數字前;

       3,當骰子結束動畫后,棋子還是移動,一步一步的向前移動,遇到四個頂點的時候自動轉彎,並最終停留在骰子步數前進的位置前;

       4,當棋子結束移動后,頁面上顯示用戶最后剩下的游戲次數,並且如果移動到有禮物的位置時彈出遮罩告訴用戶獲獎;

       5,在整個游戲期間,不允許用戶繼續點擊開始按鈕,並且當用戶剩余次數用完之后開始按鈕變色;

       游戲規則都說明了,那么需求分析就到這里了,下面介紹頁面布局:

HTML結構:

<div class="game">
    <div class="gameimg">
             <div id="pieces" class="p0-0">    <!-- 棋子 -->
                 <img src="images/pieces.png" />
             </div>
        <div class="play">
             <div class="dice">     <!-- 骰子 -->
                 <img id="dice" class="dicelo2" src="images/dice.png" />
             </div>
             <div class="playnum">
                 您今天還有<span id="pn">0</span>次游戲機會
               </div>
             <div id="playbtn">
                  <img src="images/btn_start_gray.png" />
             </div>
         </div>
    </div>
</div>

整體的結構就是一個大的div標簽,它的背景是整個棋盤,然后棋子通過相對布局定位在背景圖的18個位置上,然后骰子和開始按鈕以及說明都居中顯示在背景圖的中心。

CSS樣式:

       對於css樣式來說,主要用到的是相對定位,設置了18個位置的css樣式,而且命名規則是按照 ’p0-0‘這樣的方式來命名?為什么我要這樣呢?這樣可以用類名保存矩陣坐標的位置。棋盤上的位置1對應坐標的1-0、2對應2-0…..依此類推。最后就可以方便的設置棋子的位置了。同時,對於骰子的動畫,是通過一整張png圖片來進行定位。從骰子的1到6然后是動起來的3副圖片,一共9張圖片保存在一整張png中,然后依舊采用相對定位的方式來給用戶展示。首先是骰子開始運動,我就循環顯示最后3張動起來的圖片,然后當動畫結束后將位置停留在所要顯示對應的數字前。

11118222223這樣把部分圖片貼出來就很明顯的說明了我的意圖~

javascrpt邏輯篇:

         首先是進入頁面的加載,流程就是首先通過jsonp請求服務器,獲取該用戶上次的棋子位置,和剩余次數等基礎信息,然后渲染在頁面中,我就不在這里展開了,這里注意想說的是負責棋子移動和骰子搖動的邏輯部分。

         那么按照游戲規則,首先是骰子的搖動,當點擊開始按鈕后請求服務器結束后調用骰子執行函數,接着將服務器返回的骰子數字和到達的位置傳遞給復制骰子動畫的函數。

/**
 * 操作骰子的執行函數
 *
 * @method play_dice
 *
 * @param  {ing}  dice_num  [骰子數字]
 * @param  {int}  next_step [到達位置]
 */
function play_dice (dice_num, next_step) {
    var _stop = dice_num,
        _pande = next_step;
    diceroll($('#dice'), _stop, _pande);
}
/**
 * 搖動骰子的動畫函數
 *
 * @method diceroll
 *
 * @param  {object} dice      [骰子對象]
 * @param  {int}    num       [搖動的結果]
 * @param  {int}    _pande    [棋子停下的位置]
 */
function diceroll(dice, num, _pande){
    var i = 0,
        b = 0,
        a = function(i) {
          b = setTimeout(function() {
              dice.removeClass().addClass('dicelo' + (i % 3 + 7));   // 清除上次動畫后的點數
              i++;
              if (i > 5) {
                  // 這里的dicelo9是骰子最后一幅動畫的類名,刪除后依次添加dicelo7,dicelo8 
                  dice.removeClass('dicelo9').addClass('dicelo'+num);
                  var j = $('#pieces').attr('class').substr(1) || 1,
                      n = j + _pande;
                  var oldstep = _pande-num-1;
                  person.step = num;
                  run(person, true);//  1右,2下,3左,4上
                     clearTimeout(b);
                  i = 0;
              } else {
                  a(i);
              }
          }, 200);
        };
    a(i);
}

       這里通過一個定時器,不斷的循環骰子搖動的這個動畫,然后在執行5次之后就讓骰子停下來,同時清除定時器,然后在骰子停止搖動之后觸發棋子移動的函數,在run函數中需要兩個參數,如果第二個參數為true,就讓棋子還是移動,否則的話則只是計算棋子的矩陣坐標。而person這個對象中保存着坐標位置start: [0, 0],所要前進的步數step,和當前前進的方向de,de的值我設定為1右,2下,3左,4上 。下面是棋子移動的函數。

/**
 * 控制棋子的移動和棋盤坐標的轉換
 *
 * @method run
 *
 * @param  {object}  person [棋子移動的坐標]
 * @param  {Boolean} isAn   [棋子是否需要移動]
 *
 * @return {object}         [棋子移動的坐標]
 */
function run (person, isAn) {
    var _start = person.start,
        _step = person.step,
        _de = person.de;

    // 棋子移動過程的坐標集合
    gameList = new Array();
    for (var i = 1; i <= _step; i++) {
        if (_de == 1) {  // 棋子向右移動
            _start[0] += 1;
            if (_start[0] == 5) {
                _de = 2;
            }
        }else if (_de == 2) {  // 棋子向下移動
            _start[1] += 1;
            if (_start[1] == 4) {
                _de = 3;
            }
        }else if (_de == 3) {  // 棋子向左移動
            _start[0] += -1;
            if (_start[0] == 0) {
                _de = 4;
            }
        }else if (_de == 4) {  // 棋子向上移動
            _start[1] += -1;
            if (_start[1] == 0) {
                _de = 1;
            }
        }
        gameList.push([_start[0], _start[1]]);  // 將移動后的結果保存在集合中
    }

    // 當需要棋子移動時執行
    if (isAn) {
        var j = 0,
            d = 0,
            c = function(j) {
              d = setTimeout(function() {
                  $('#pieces').attr('class', null).addClass('p'+gameList[0][0] + '-' +gameList[0][1]);
                  gameList.shift(0);
                  j++;
                  if (gameList.length == 0) { // 如果棋子移動結束后則彈出相應的結果
                          returndice(_start);
                      clearTimeout(d);
                  } else {
                      c(j);
                  }
              }, 500);
            };
        c(j);
    }
    person.start = _start;
    person.step = 0;
    person.de = _de;
    return person;
}

      這里依舊是使用定時器來重復顯示棋子的移動過程,由於定時器是異步執行的,所以我將函數產生的一個運動結果保存在數組中,然后在異步的定時器中一個個的遍歷數組中的結果,每次取出數組中的第一個結果,執行移動之后便移除第一個結果,后面的結果頂上來,下次取出的第一個結果就是下一次需要移動的結果,最終在結束移動的時候及隊列清空的情況下清除定時器,然后我在代碼中執行了returndice函數來顯示移動后的結果,如果中獎了則調出遮罩提示用戶的獲獎情況。

總結:

       到這里,全部的邏輯基本都說明白了,其實整體來說就是3個函數的循環調用,為了保證在移動端上的性能,在每次定時器結束之后都需要及時的清除掉定時器,否則會出現定時器覆蓋的情況,在我測試的時候如果沒有及時清除定時器的話,在執行20000+的時候會出現移動的步數比實際的情況少一步的情況,最開始的時候我在最后寫了校驗函數來保證最終棋子停下來的位置與服務器返回的一致,本來以為是誤差,結果后來再測試的時候發現校驗的時候會出現棋子閃現的情況,這是很不好的用戶體驗,而且按照我棋子移動的邏輯來看,是不可能出現錯位的,所以理論上是不需要校驗函數的,所以再經過仔細查看代碼后發現了第二次棋子移動的定時器在使用后並沒有及時清除,在用戶游戲次數較少的時候是很難出現bug的,但是自動測試的時候接近於無限的次數很容易出現錯誤的情況,所以還是很重要的bug,還好及時發現。。。o(︶︿︶)o 最后為了保證在一次游戲的過程中用戶不能繼續點擊開始按鈕,所以在開始搖骰子的時候將全局鎖設置為false,並在最后棋子結束移動之后再打開全局鎖。到此為止,所有的游戲規則都滿足了~~

      今天就到這里吧。。。下次帶來一個頁面跑馬燈的效果~~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM