45fan.com - 路饭网

搜索: 您的位置主页 > 电脑频道 > 编程代码 > 阅读资讯:Javascript 2048小游戏的实例分享

Javascript 2048小游戏的实例分享

2015-07-24 07:59:47 来源:www.45fan.com 【

Javascript 2048小游戏的实例分享

去年2048很火, 本来我也没玩过, 同事说如果用JS写2048 只要100多行代码;

今天试了一下, 逻辑也不复杂, 主要是数据构造函数上的数据的各种操作, 然后通过重新渲染DOM实现界面的更新, 整体不复杂, JS,css,和HTML合起来就300多行;

界面的生成使用了underscore.js的template方法, 使用了jQuery,主要是DOM的选择和操作以及动画效果,事件的绑定只做了PC端的兼容,只绑定了keydown事件;

把代码放到github-page上, 通过点击这里查看 实例: 打开2048实例;

效果图如下:

Javascript 2048小游戏的实例分享

所有的代码分为两大块,Data, View;

Data是构造函数, 会把数据构造出来, 数据会继承原型上的一些方法;

View是根据Data的实例生成视图,并绑定事件等, 我直接把事件认为是controller了,和View放在了一起, 没必要分开;

Data的结构如下:

  /**
   * @desc 构造函数初始化
   * */
  init : function
  /**
   * @desc 生成了默认的数据地图
   * @param void
   * */
  generateData : function
  /**
   * @desc 随机一个block填充到数据里面
   * @return void
   * */
  generationBlock : function
  /**
   * @desc 获取随机数 2 或者是 4
   * @return 2 || 4;
   * */
  getRandom : function
  /**
   * @desc 获取data里面数据内容为空的位置
   * @return {x:number, y:number}
   * */
  getPosition : function
  /**
   * @desc 把数据里第y排, 第x列的设置, 默认为0, 也可以传值;
   * @param x, y
   * */
  set : function
  /**
   * @desc 在二维数组的区间中水平方向是否全部为0
   * @desc i明确了二维数组的位置, k为开始位置, j为结束为止
   * */
  no_block_horizontal : function
  no_block_vertica : function
  /**
   * @desc 往数据往左边移动,这个很重要
   * */
  moveLeft : function
  moveRight : function
  moveUp : function
  moveDown : function

有了数据模型,那么视图就简单了,主要是用底线库underscore的template方法配合数据生成html字符串,然后对界面进行重绘:

View的原型方法:
renderHTML : function //生成html字符串,然后放到界面中
init : function //构造函数初始化方法
bindEvents : function //给str绑定事件, 认为是控制器即可

因为原始的2048有方块的移动效果, 我们独立起来了一个服务(工具方法,这个工具方法会被View继承), 主要是负责界面中的方块的移动, getPost是给底线库用的, 在模板生成的过程中需要根据节点的位置动态生成横竖坐标,然后定位:

 var util = {
  animateShowBlock : function() {
   setTimeout(function() {
    this.renderHTML();
   }.bind(this),200);
  },
  animateMoveBlock : function(prop) {
   $("#num"+prop.form.y+""+prop.form.x).animate({top:40*prop.to.y,left:40*prop.to.x},200);
  },
  //底线库的模板中引用了这个方法;
  getPost : function(num) {
   return num*40 + "px";
  }
  //这个应该算是服务;
 };

下面是全部的代码, 引用的JS使用了CDN,可以直接打开看看:

<!DOCTYPE html>
<html>
<head lang="en">
 <meta charset="UTF-8">
 <title></title>
</head>
<body>
<script src="http://cdn.bootcss.com/underscore.js/1.8.3/underscore-min.js"></script>
<script src="http://cdn.bootcss.com/jquery/2.1.4/jquery.js"></script>
<style>
 #g{
  position: relative;
 }

 .block,.num-block{
  position: absolute;
  width: 40px;
  height: 40px;
  line-height: 40px;
  text-align: center;
  border-radius: 4px;
 }
 .block{
  border:1px solid #eee;
  box-sizing: border-box;
 }
 .num-block{
  color:#27AE60;
  font-weight: bold;
 }
</style>
 <div class="container">
  <div class="row">
   <div id="g">
   </div>
  </div>
 </div>

<script id="tpl" type="text/template">
 <% for(var i=0; i<data.length; i++) {%>
   <!--生成背景块元素--->
  <% for(var j=0; j< data[i].length; j++ ) { %>
   <div id="<%=i%><%=j%>" class="block" style="left:<%=util.getPost(j)%>;top:<%=util.getPost(i)%>" data-x="<%=j%>" data-y="<%=i%>" data-info='{"x":<%=[j]%>,"y":<%=[i]%>}'>
   </div>
  <% } %>
   <!--生成数字块元素--->
  <% for(var j=0; j< data[i].length; j++ ) { %>
   <!--如果数据模型里面的值为0,那么不显示这个数据的div--->
   <% if ( 0!==data[i][j] ) {%>
    <div id="num<%=i%><%=j%>" class="num-block" style="left:<%=util.getPost(j)%>;top:<%=util.getPost(i)%>" >
     <%=data[i][j]%>
    </div>
   <% } %>
  <% } %>
 <% } %>
</script>
<script>
 var Data = function() {
  this.init();
 };
 $.extend(Data.prototype, {
  /**
   * @desc 构造函数初始化
   * */
  init : function() {
   this.generateData();
  },
  /**
   * @desc 生成了默认的数据地图
   * @param void
   * */
  generateData : function() {
   var data = [];
   for(var i=0; i<4; i++) {
    data[i] = data[i] || [];
    for(var j=0; j<4; j++) {
     data[i][j] = 0;
    };
   };
   this.map = data;
  },
  /**
   * @desc 随机一个block填充到数据里面
   * @return void
   * */
  generationBlock : function() {
   var data = this.getRandom();
   var position = this.getPosition();
   this.set( position.x, position.y, data)
  },
  /**
   * @desc 获取随机数 2 或者是 4
   * @return 2 || 4;
   * */
  getRandom : function() {
   return Math.random()>0.5 ? 2 : 4;
  },
  /**
   * @desc 获取data里面数据内容为空的位置
   * @return {x:number, y:number}
   * */
  getPosition : function() {
   var data = this.map;
   var arr = [];
   for(var i=0; i<data.length; i++ ) {
    for(var j=0; j< data[i].length; j++ ) {
     if( data[i][j] === 0) {
      arr.push({x:j, y:i});
     };
    };
   };
   return arr[ Math.floor( Math.random()*arr.length ) ];
  },
  /**
   * @desc 把数据里第y排, 第x列的设置, 默认为0, 也可以传值;
   * @param x, y
   * */
  set : function(x,y ,arg) {
   this.map[y][x] = arg || 0;
  },
  /**
   * @desc 在二维数组的区间中水平方向是否全部为0
   * @desc i明确了二维数组的位置, k为开始位置, j为结束为止
   * */
  no_block_horizontal: function(i, k, j) {
   k++;
   for( ;k<j; k++) {
    if(this.map[i][k] !== 0)
    return false;
   };
   return true;
  },
  //和上面一个方法一样,检测的方向是竖排;
  no_block_vertical : function(i, k, j) {
   var data = this.map;
   k++;
   for(; k<j; k++) {
    if(data[k][i] !== 0) {
     return false;
    };
   };
   return true;
  },
  /**
   * @desc 往左边移动
   * */
  moveLeft : function() {
   /*
   * 往左边移动;
   * 从上到下, 从左到右, 循环;
   * 从0开始继续循环到当前的元素 ,如果左侧的是0,而且之间的空格全部为0 , 那么往这边移,
   * 如果左边的和当前的值一样, 而且之间的空格值全部为0, 就把当前的值和最左边的值相加,赋值给最左边的值;
   * */
   var data = this.map;
   var result = [];
   for(var i=0; i<data.length; i++ ) {
    for(var j=1; j<data[i].length; j++) {
     if (data[i][j] != 0) {
      for (var k = 0; k < j; k++) {
       //当前的是data[i][j], 如果最左边的是0, 而且之间的全部是0
       if (data[i][k] === 0 && this.no_block_horizontal(i, k, j)) {
        result.push( {form : {y:i,x:j}, to :{y:i,x:k}} );
        data[i][k] = data[i][j];
        data[i][j] = 0;
        //加了continue是因为,当前的元素已经移动到了初始的位置,之间的循环我们根本不需要走了
        break;
       }else if(data[i][j]!==0 && data[i][j] === data[i][k] && this.no_block_horizontal(i, k, j)){
        result.push( {form : {y:i,x:j}, to :{y:i,x:k}} );
        data[i][k] += data[i][j];
        data[i][j] = 0;
        break;
       };
      };
     };
    };
   };
   return result;
  },
  moveRight : function() {
   var result = [];
   var data = this.map;
   for(var i=0; i<data.length; i++ ) {
    for(var j=data[i].length-2; j>=0; j--) {
     if (data[i][j] != 0) {
      for (var k = data[i].length-1; k>j; k--) {
       //当前的是data[i][j], 如果最左边的是0, 而且之间的全部是0
       if (data[i][k] === 0 && this.no_block_horizontal(i, k, j)) {
        result.push( {form : {y:i,x:j}, to :{y:i,x:k}} );
        data[i][k] = data[i][j];
        data[i][j] = 0;
        break;
       }else if(data[i][k]!==0 && data[i][j] === data[i][k] && this.no_block_horizontal(i, j, k)){
        result.push( {form : {y:i,x:j}, to :{y:i,x:k}} );
        data[i][k] += data[i][j];
        data[i][j] = 0;
        break;
       };
      };
     };
    };
   };
   return result;
  },
  moveUp : function() {
   var data = this.map;
   var result = [];
   // 循环要检测的长度
   for(var i=0; i<data[0].length; i++ ) {
    // 循环要检测的高度
    for(var j=1; j<data.length; j++) {
     if (data[j][i] != 0) {
      //x是确定的, 循环y方向;
      for (var k = 0; k<j ; k++) {
       //当前的是data[j][i], 如果最上面的是0, 而且之间的全部是0
       if (data[k][i] === 0 && this.no_block_vertical(i, k, j)) {
        result.push( {form : {y:j,x:i}, to :{y:k,x:i}} );
        data[k][i] = data[j][i];
        data[j][i] = 0;
        break;
       }else if(data[j][i]!==0 && data[k][i] === data[j][i] && this.no_block_vertical(i, k, j)){
        result.push( {form : {y:j,x:i}, to :{y:k,x:i}} );
        data[k][i] += data[j][i];
        data[j][i] = 0;
        break;
       };
      };
     };
    };
   };
   return result;
  },
  moveDown : function() {
   var data = this.map;
   var result = [];
   // 循环要检测的长度
   for(var i=0; i<data[0].length; i++ ) {
    // 循环要检测的高度
    for(var j=data.length - 1; j>=0 ; j--) {
     if (data[j][i] != 0) {
      //x是确定的, 循环y方向;
      for (var k = data.length-1; k>j ; k--) {
       if (data[k][i] === 0 && this.no_block_vertical(i, k, j)) {
        result.push( {form : {y:j,x:i}, to :{y:k,x:i}} );
        data[k][i] = data[j][i];
        data[j][i] = 0;
        break;
       }else if(data[k][i]!==0 && data[k][i] === data[j][i] && this.no_block_vertical(i, j, k)){
        result.push( {form : {y:j,x:i}, to :{y:k,x:i}} );
        data[k][i] += data[j][i];
        data[j][i] = 0;
        break;
       };
      };
     };
    };
   };
   return result;
  }
  });
 var util = {
  animateShowBlock : function() {
   setTimeout(function() {
    this.renderHTML();
   }.bind(this),200);
  },
  animateMoveBlock : function(prop) {
   $("#num"+prop.form.y+""+prop.form.x).animate({top:40*prop.to.y,left:40*prop.to.x},200);
  },
  //底线库的模板中引用了这个方法;
  getPost : function(num) {
   return num*40 + "px";
  }
  //这个应该算是服务;
 };
 var View = function(data) {
  this.data = data.data;
  this.el = data.el;
  this.renderHTML();
  this.init();
 };
 $.extend(View.prototype, {
  renderHTML : function() {
   var str = _.template( document.getElementById("tpl").innerHTML )( {data : this.data.map} );
   this.el.innerHTML = str;
  },
  init : function() {
   this.bindEvents();
  },
  bindEvents : function() {
   $(document).keydown(function(ev){
    var animationArray = [];
    switch(ev.keyCode) {
     case 37:
      animationArray = this.data.moveLeft();
      break;
     case 38 :
      animationArray = this.data.moveUp();
      break;
     case 39 :
      animationArray = this.data.moveRight();
      break;
     case 40 :
      animationArray = this.data.moveDown();
      break;
    };
    if( animationArray ) {
     for(var i=0; i<animationArray.length; i++ ) {
      var prop = animationArray[i];
      this.animateMoveBlock(prop);
     };
    };
    this.data.generationBlock();
    this.animateShowBlock();
   }.bind(this));
  }
 });

 $(function() {
  var data = new Data();
  //随机生成两个节点;
  data.generationBlock();
  data.generationBlock();
  //生成视图
  var view = new View({ data :data, el : document.getElementById("g") });
  //继承工具方法, 主要是动画效果的继承;
  $.extend( true, view, util );
  //显示界面
  view.renderHTML();
 });
</script>
</body>
</html>

以上所述就是本文的全部内容了,希望大家能够喜欢。


本文地址:http://www.45fan.com/bcdm/15512.html
Tags: JavaScript 编写 2048
编辑:路饭网
推广内容
推荐阅读
热门推荐
推荐文章
关于我们 | 联系我们 | 友情链接 | 网站地图 | Sitemap | App | 返回顶部