十一马上就要到了~~做为前端的小伙伴~活动页面必然是少不了了~那怎么吸引眼球呢?当然是玩游戏啦~这次就带来一个我为十一做的小游戏,作为活动页中的衬托~~
那我们要做个什么样的游戏呢?先上图~
由于实际的效果图因为是公司的活动,我就不贴出来了~这里用这个棋盘来大致说明一下效果咯~下面是游戏规则:
1,当用户看到棋盘的时候,棋子出现在上一次停留下来的位置;
2,当用户点击开始后,中间的骰子开始摇动,最后停止在服务器返回的数字前;
3,当骰子结束动画后,棋子还是移动,一步一步的向前移动,遇到四个顶点的时候自动转弯,并最终停留在骰子步数前进的位置前;
4,当棋子结束移动后,页面上显示用户最后剩下的游戏次数,并且如果移动到有礼物的位置时弹出遮罩告诉用户获奖;
5,在整个游戏期间,不允许用户继续点击开始按钮,并且当用户剩余次数用完之后开始按钮变色;
游戏规则都说明了,那么需求分析就到这里了,下面介绍页面布局:
<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样式来说,主要用到的是相对定位,设置了18个位置的css样式,而且命名规则是按照 ’p0-0‘这样的方式来命名?为什么我要这样呢?这样可以用类名保存矩阵坐标的位置。棋盘上的位置1对应坐标的1-0、2对应2-0…..依此类推。最后就可以方便的设置棋子的位置了。同时,对于骰子的动画,是通过一整张png图片来进行定位。从骰子的1到6然后是动起来的3副图片,一共9张图片保存在一整张png中,然后依旧采用相对定位的方式来给用户展示。首先是骰子开始运动,我就循环显示最后3张动起来的图片,然后当动画结束后将位置停留在所要显示对应的数字前。
首先是进入页面的加载,流程就是首先通过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,并在最后棋子结束移动之后再打开全局锁。到此为止,所有的游戏规则都满足了~~
今天就到这里吧。。。下次带来一个页面跑马灯的效果~~