45fan.com - 路饭网

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

JavaScript 连连看小游戏的实例分享

2015-07-24 08:27:45 来源:www.45fan.com 【

JavaScript 连连看小游戏的实例分享

天天看到别人玩连连看, 表示没有认真玩过, 不就把两个一样的图片连接在一起么, 我自己写一个都可以呢。

使用Javascript写了一个, 托管到github, 在线DEMO地址查看:打开

最终的效果图:

JavaScript 连连看小游戏的实例分享

写连连看之前要先考虑哪些呢?

1:如何判断两个元素可以连接呢, 刚刚开始的时候我也纳闷, 可以参考这里:打开;

2:模板引擎怎么选择呢, 我用了底线库的template,因为语法简单。 本来想用Handlebars,但是这个有点大啊, 而且底线库也提供很多常用工具方法( •̀ ω •́ )y;

3:布局如何布局呢, 用table, td加上边框, 边框内部一个div,div就是连连看的棋子, 界面更清爽, 简单, 其实直接用canvas写也行, 没认真研究过canvas;

4:两个元素连接时连线的效果我们要怎么实现呢,如果用dom实现那么需要用到图片,元素连接时候把图片定位到连接的路径。 或者用canvas, 直接用canvas把连接的效果画出来, 我选择后者;

因为我不考虑低浏览器, 使用了zeptoJS库, 基于习惯,把bootstrap也引用了;

使用了三个主要构造函数, 包括Data, View, Score;

View的结构如下, 东西比较少 包括事件绑定, 界面生成, 以及当两个相同元素消失时的 绘图效果:

View

/**
 * @desc 根据数据生成map
 * */
 renderHTML : function

/**
* @desc 界面的主要事件绑定
* @return this;
* */
 bindEvents : function


/**
* @desc 工具方法,在canvas上面进行绘图;
* @param [{x:0,y:0},{x:1,y:1},{x:2,y:2},{x:3,y:3}]一个数组, 会自动重绘;
* */
showSparkLine : function

tbody内部元素的模板是这样的:

<script type="text/template" id="tr-td-tpl">
 <% for(var i=0; i<data.length; i++) {%>
  <tr>
   <% for(var j=0; j< data[i].length; j++ ) { %>
    <td id="<%=i%><%=j%>" class="bg<%=data[i][j]%>" data-x="<%=j%>" data-y="<%=i%>" data-data="<%=data[i][j]%>" data-info='{"x":<%=[j]%>,"y":<%=[i]%>}'>
     <div>
      <%=getImg(data[i][j])%>
     </div>
    </td>
   <% } %>
  </tr>
 <% } %>
</script>

上面代码的getImg方法会调用全局window的getImg方法,这个方法是根据数据生成图片字符串, 是一个辅助的函数:

 window.getImg = function( num ) {
  switch(num){
   case 1:
    return "<img src='imgs/ani (1).gif' />";
   case 2:
    return "<img src='imgs/ani (2).gif' />";
   case 3:
    return "<img src='imgs/ani (3).gif' />";
   case 4:
    return "<img src='imgs/ani (4).gif' />";
   case 5:
    return "<img src='imgs/ani (5).gif' />";
   case 6:
    return "<img src='imgs/ani (6).gif' />";
  }
 };

因为连连看的数据是个二维的数组, 所以模板中必须使用两个for循环, 循环产生HTML字符串, 如果把数据和模板合在一起, 会生成下图的DOM结构:

JavaScript 连连看小游戏的实例分享

分数模块构造函数Score, 所有有关得分的代码就这些了 (把元素传进去, 直接调用生成实例的addScore方法, 会自动渲染DOM), 为分数单独写一个构造函数是因为为了解耦:

   Score = function(el) {
    this.el = $(el);
    this.score = 0;
   };

 $.extend( Score.prototype , {
  /**
   * @desc 改变元素的HTML,递增分数;
   * @param
   * */
  addScore : function() {
   this.el.html(++this.score);
  }
 });

构造函数Data, 主要的结构如下 , 虽然方法比较少, 实际上Data这块代码占了300行.... 要判断元素是否可以连接用canConnect方法,canConnect方法又会调用dirConnect方法, 计算比较繁琐, 想了解的话最好自己写写:

//新建初始化
newData : function

//工具方法,随机混肴数组;
suffer : function

 /**
* @desc set值,把地图中对应的数据清空或者设置,两用接口
 * @param x, y
* @return chain
* */
set : function

/**
 * @desc 判断两个元素之间是否可以连接
* @param [{x:1,y:1},{x:1,y:1}]
* @return false || []
* */
canConnect : function

/**
* @desc 判断元素是否可以直连
* @param [{x:1,y:1},{x:1,y:1}]
* @return false || true
* */
dirConnect

所有所有代码如下, 作为参考:

<!DOCTYPE html>
<html>
<head lang="en">
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
 <!-- 新 Bootstrap 核心 CSS 文件 -->
 <link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.4/css/bootstrap.min.css">
 <title>link</title>
 <script src="js/zepto.js"></script>
 <script src="js/underscore1.8.js"></script>
 <style>
  table{
   border-collapse: collapse;
  }
  td{
   border:1px solid #f5f5f5;
   text-align: center;
   line-height: 40px;
   cursor: pointer;
  }
  td.active{
   opacity: 0.7;
  }
  td div{
   width:40px;
   height:40px;
  }
  .bg1{
   /*background: #2ECC71;*/
  }
  .bg2{
   /*background: #E67E22;*/
  }
  .bg3{
   /*background: #34495E;*/
  }
  .bg4{
   /*background: #1ABC9C;*/
  }
  .relative{
   position: relative;
  }
  .absolute{
   position: absolute;
   left:0;
   top:0;
  }
 </style>
</head>
<body>

<div class="container ">
 <div class="row" style="width:80%;margin:0 auto;">
  <h3>得分<span class="label label-default" id="score">0</span></h3>
 </div>
</div>

<div class="container">
 <div class="row relative">
  <table class="absolute">
   <thead></thead>
   <tbody id="tbody">
   </tbody>
  </table>
  <canvas id="canvas">
   <p>Your browserdoes not support the canvas element.</p>
  </canvas>
 </div>
</div>
<script type="text/template" id="tr-td-tpl">
 <% for(var i=0; i<data.length; i++) {%>
  <tr>
   <% for(var j=0; j< data[i].length; j++ ) { %>
    <td id="<%=i%><%=j%>" class="bg<%=data[i][j]%>" data-x="<%=j%>" data-y="<%=i%>" data-data="<%=data[i][j]%>" data-info='{"x":<%=[j]%>,"y":<%=[i]%>}'>
     <div>
      <%=getImg(data[i][j])%>
     </div>
    </td>
   <% } %>
  </tr>
 <% } %>
</script>
<script>
 var el = document.getElementById("tbody");
 var elCan = document.getElementById("canvas");
 var tpl = document.getElementById("tr-td-tpl");

 var cfg = {
  width : 8,
  height : 8
 };
 window.getImg = function( num ) {
  switch(num){
   case 1:
    return "<img src='imgs/ani (1).gif' />";
   case 2:
    return "<img src='imgs/ani (2).gif' />";
   case 3:
    return "<img src='imgs/ani (3).gif' />";
   case 4:
    return "<img src='imgs/ani (4).gif' />";
   case 5:
    return "<img src='imgs/ani (5).gif' />";
   case 6:
    return "<img src='imgs/ani (6).gif' />";
  }
 };

 var View = function(data, score) {
   this.data = data;
   this.score = score;
   },
   Data = function(cfg) {
   this.cfg = {
    width : cfg.width+2,
    height : cfg.height+2
   };
    this.getRandom = this.getRandom();
   },
   Score = function(el) {
    this.el = $(el);
    this.score = 0;
   };

 $.extend( Data.prototype, {
  /**
   * @desc 把两个
   * @param HTMLELEMENT
   * @return true || false
   * */
  clear : function(obj, target) {
  },
  /**
   * @desc 根据this.cfg新建数据到this.map
   * @param void
   * @return void
   * */
  newData : function() {
   var result = [];
   for(var i=0; i<=this.cfg.height+1; i++ ) {
    result[i] = result[i] || [];
    for(var j = 0; j<= this.cfg.width+1; j++) {

     if(i === 0 || j===0 || (i===this.cfg.height+1) || j === (this.cfg.width+1) ) {
      result[i][j] = 0;
     }else{
      //1-4
      result[i][j] = this.getRandom();
     }
    };
   };
   this.map = result;
   return this;
  },
  //随机混肴数组;
  suffer : function(obj) {
   function random(min, max) {
    if (max == null) {
     max = min;
     min = 0;
    }
    return min + Math.floor(Math.random() * (max - min + 1));
   };
   var set = obj;
   var length = set.length;
   var shuffled = Array(length);
   for (var index = 0, rand; index < length; index++) {
    rand = random(0, index);
    if (rand !== index) shuffled[index] = shuffled[rand];
    shuffled[rand] = set[index];
   }
   return shuffled;
  },
  /**
   * @return 返回值必须是成双的, 消除到最后尼玛,发现有一堆不匹配的,玩个球;
   * */
  getRandom : function() {
   //如果消消乐是3*3, 那么你告诉我....最后一个和谁消, 所以要做的就是把所有的元素生成变成一半,然后返回;
   var arr = new Array( (this.cfg.height) * (this.cfg.width) / 2 );
   var result = [];
   for(var i=0; i<arr.length; i++ ) {
    arr[i] = (Math.floor( Math.random()*6 ) + 1);
   };
   result = Array.prototype.concat.call( [] , arr, arr);
   result = this.suffer( result );
   return function( ) {
    return result.pop();
   };
  },
  /**
   * @desc set值
   * @param x, y
   * @return chain
   * */
  set : function( x, y) {
   this.map[y][x] = 0;
   return this;
  },
  /**
   * @desc 判断元素是否可以连接
   * @param [{x:1,y:1},{x:1,y:1}]
   * @return false || true
   * */
  canConnect : function(obj,target) {
   var map = this.map;
   //循环obj的y轴相等 , obj.x旁边所有数据为0的元素;;
   var getX = function( obj ) {
    var result = [];
    //循环找出在X附近为0的元素;
    for(var i=obj.x+1; i< map[0].length; i++) {
     if( map[obj.y][i] == 0 ) {
      result.push( {x:i, y:obj.y} );
     }else{
      break;
     };
    };
    for(var i=obj.x-1; i>=0; i--) {
     if( map[obj.y][i] == 0 ) {
      result.push( {x:i,y:obj.y} );
     }else{
      break;
     };
    };
    return result;
   };
   //循环obj的x轴相等, obj.y旁边所有数据为0的元素;
   var getY = function(obj) {
    var result = [];
    for(var i=obj.y+1; i<map.length; i++) {
     if( map[i][obj.x] == 0) {
      result.push( { x : obj.x ,y : i} );
     }else{
      break;
     };
    };
    for(var i=obj.y-1; i>=0; i--) {
     if( map[i][obj.x] == 0 ) {
      result.push( { x : obj.x ,y : i} );
     }else{
      break;
     };
    };
    return result;
   };
   var arr0 = Array.prototype.concat.call( [], getX(obj), obj, getY(obj)).filter(function(obj) {
    return !!obj;
   });
   var arr1 = Array.prototype.concat.call( [], getX(target), target, getY(target) ).filter(function(obj) {
    return !!obj;
   });
   for(i = 0; i<arr0.length; i++) {
    for(var j = 0; j<arr1.length; j++) {
     //只要有一个连接就返回true;
     if( this.dirConnect(arr0[i],arr1[j]) ) {
      return [obj, arr0[i], arr1[j], target];
     };
    };
   };
   return false;
  },
  /**
   * @desc 判断元素是否可以直接连接
   * @param [{x:1,y:1},{x:1,y:1}]
   * @return false || true
   * */
  dirConnect : function(obj, target) {
   var map = this.map;
   //row是x轴 列
   //col是y轴 行
   var min = 0, max = 0, sum = 0;
   if(obj.y === target.y) {
    if(obj.x < target.x) {
     min = obj.x;
     max = target.x;
    }else{
     min = target.x;
     max = obj.x;
    };
    for(var i=min; i<=max; i++) {
     sum += map[obj.y][i];
    };
    if(sum === (map[obj.y][obj.x] + map[target.y][target.x])) {
     return true;
    }else{
     return false;
    };
   };
   if(obj.x === target.x) {
    if(obj.y < target.y) {
     min = obj.y;
     max = target.y;
    }else{
     min = target.x;
     max = obj.y;
    };
    for( i=min; i<=max; i++) {
     sum += map[i][obj.x];
    };
    if( sum === (map[obj.y][obj.x] + map[target.y][target.x])) {
     return true;
    }else{
     return false;
    };
   };
  }
 });
 $.extend( View.prototype, {
  /**
   * @desc 为view添加视图的主元素
   * @return void
   * */
  setEL : function(el) {
   this.el = el;
   return this;
  },
  setTpl : function(tpl) {
   this.tpl = _.template( tpl.innerHTML );
   return this;
  },
  /**
   * @desc 根据数据生成map
   * */
  renderHTML : function() {
   $(this.el).html( this.tpl( {data : this.data.map} ) );
   return this;
  },
  /**
   * @desc 界面的主要事件绑定
   * @return this;
   * */
  bindEvents : function() {
   $(this.el).delegate("td", "click", this.click.bind(this));
   return this;
  },
  /**
   * @desc click事件, 单独抽出来的;
   * */
  click : function(ev) {
   //修改样式;
   $("td.active").removeClass("active");
   var target = $(ev.target).closest("td");
   target.addClass("active");

   //第一次点击我们做的特殊处理;
   var prev = this.prev;
   if( !prev || target[0] === prev[0]){
    this.prev = target;
    return;
   };
   
   if( prev.attr("data-data") === target.attr("data-data")) {
    var xy = JSON.parse( prev.attr("data-info") );
    var xxyy = JSON.parse( target.attr("data-info") );
    //保存了连接的数组信息
    var connectionInfo = [] || false;
    if( connectionInfo = this.data.canConnect( xy, xxyy) ) {
     this.showSparkLine( connectionInfo );
     this.prev = undefined;
     this.data.set(xy.x, xy.y);
     this.data.set(xxyy.x, xxyy.y);
     this.score.addScore();
     var _this = this;
     setTimeout(function() {
      _this.renderHTML();
     },2000);
    };
    prev.attr("data-data", "");
    target.attr("data-data","")
   }else{
    this.prev = target;
   };
  },
  /**
   * @desc 工具方法,在canvas上面进行绘图;
   * @param [{x:0,y:0},{x:1,y:1},{x:2,y:2},{x:3,y:3}]一个数组, 会自动重绘;
  * */
  showSparkLine : function( arr ) {
   arr = arr.map(function(xy) {
    return {
     x : (xy.x)*40 + 20,
     y : (xy.y)*40 + 20
    }
   });
   var elCan = document.getElementById("canvas");
   function spark(ctx) {
    function showAndClear(arr, lineWidth) {
     ctx.clearRect(0,0,elCan.width,elCan.height);
     ctx.beginPath();
     ctx.lineJoin = "round";
     ctx.lineWidth = lineWidth;
     ctx.shadowColor = "rgba(241, 196, 15, 0.41)";
     ctx.shadowOffsetX = 1;
     ctx.shadowOffsetY = 1;
     ctx.shadowBlur = 1;
     for(var i=0; i<arr.length-1; i++) {
      var xy = arr[i];
      var nextXY = arr[i+1]
      ctx.moveTo(xy.x, xy.y);
      ctx.lineTo(nextXY.x, nextXY.y);
     };
     ctx.stroke();
    };
    var ctx = elCan.getContext("2d");
    ctx.strokeStyle = "#F1C40F";
    var lineWidthArr = [1,2,1,2,1,3,1,0];
    var len = lineWidthArr.length;
    var times = 400, addTimes = 200;
    while(len--) {
     (function(len){
      setTimeout(function() {
       showAndClear(arr, lineWidthArr[len]);
       if(len==0) {
        ctx.clearRect(0,0,elCan.width,elCan.height);
       }
      }, times);
      times += addTimes;
     })(len)
    };
   };
   spark( elCan );
  }
 });
 $.extend( Score.prototype , {
  /**
   * @desc 改变元素的HTML,递增分数;
   * @param
   * */
  addScore : function() {
   this.el.html(++this.score);
  }
 });

 $(function() {
  var score = new Score( document.getElementById("score") );
  var data = new Data(cfg).newData();
  var view = new View(data, score);
  view.setEL( el ).setTpl( tpl).renderHTML().bindEvents();
  (function init() {
   //如果通过style属性添加width或者height,会根据原来的宽和高度自动伸缩的
   elCan.width = el.offsetWidth;
   elCan.height = el.offsetHeight;
  })();
 });

</script>
</body>
</html>

在线DEMO地址查看:打开

找到了一个别人写的连连看, 代码极少, 作为参考吧:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
 <title> 连连看 </title>
 <meta name="Generator" content="EditPlus">
 <meta name="Author" content="">
 <meta name="Keywords" content="">
 <meta name="Description" content="">
 <style type="text/css">
  #board{width:508px; height:500px; margin: 30px auto 0px; overflow: hidden; position: relative; background-color: #999999;}
  #board span{display: block; position: absolute; width: 30px; height: 30px; }
 </style>
</head>
<body>
<div id="board" >
</div>
</body>

<!--    js    -->
<script src="http://cdn.bootcss.com/jquery/2.1.4/jquery.min.js"></script>
<script type="text/javascript" >

 $(function(){
  var cont=$("#board");
  var colors=["#ff0000","#00ff00","#0000ff","#ffcc33","#000000","#00ffcc","#ffffff"];
  var pos=[];
  var click=0;
  var firstSpan;
  var fx;
  var fy;
  var arr=[];

  arr=[0,0,0,0,0,0,0,0];
  pos.push(arr);

  for(var i=0;i<8;i++){
   new creSpan(i,cont,0,i*40,colors[6],0);
  }

  for(var i=1;i<=6;i++){
   m=new creSpan(i,cont,i*40,0,"#ffffff");
   arr=[0];

   for(var j=0;j<6;j++){
    var color=Math.floor(Math.random()*6);
    new creSpan(i,cont,i*40,(j+1)*40,colors[color],(color+1));
    arr.push(1);
   }
   m=new creSpan(i,cont,i*40,(j+1)*40,"#ffffff",0);
   arr.push(0);
   pos.push(arr);

  }
  for(var i=0;i<8;i++){
   m=new creSpan(i,cont,7*40,i*40,"#ffffff",0);
  }
  arr=[0,0,0,0,0,0,0,0];
  pos.push(arr);

  function clear(c1,c2,x,y){
   if(c1!=null)c1.style.background="#ffffff";
   if(c2!=null){
    c2.style.background="#ffffff";
    pos[x-1][y-1]=0;
    pos[fx-1][fy-1]=0;
   }
   fx=0;
   fy=0;
   click=0;
  }

  $.each($("#board span"),function(index,mSpan){
   $(this).click(function(){
    var x=Math.floor(index/8);
    var y=Math.floor(index%8);
    if(click==0){
     click=1;
     firstSpan=mSpan;
     fx=x;
     fy=y;
     return;
    }

    if(firstSpan.id!=mSpan.id||(x==fx&&fy==y)){
     clear(null,null,0,0);
     return;
    }
    var col=6;
    var row=6;

    for(var i=0;i<row+2;i++){
     var step=i-x>0?1:-1;
     var count=0;
     for(var j=x;j!=i;j+=step){
      count+=pos[j][y];
     }
     step=y>fy?-1:1;
     for(j=y;j!=fy;j+=step){
      count+=pos[i][j];
     }
     step=i>fx?-1:1;
     for(j=i;j!=fx;j+=step){
      count+=pos[j][fy];
     }
     if(count==1){
      clear(firstSpan,mSpan,x,y);
      return;
     }
    }
    for(i=0;i<col+2;i++){
     step=i-y>0?1:-1;
     count=0;
     for(j=y;j!=i;j+=step){
      count+=pos[x][j];
     }
     step=x>fx?-1:1;
     for(j=x;j!=fx;j+=step){
      count+=pos[i][j];
     }
     step=i<fy?1:-1;
     for(j=i;j!=fy;j+=step){
      count+=pos[fx][j];
     }
     if(count==1){
      clear(firstSpan,mSpan,x,y);
      return;
     }
    }
    clear(null,null,0,0);

   });
  });
 });

 function creSpan(n,cont,mtop,mleft,mcolor,idstr){
  var mSpan=document.createElement("span");
  cont[0].appendChild(mSpan);
  mSpan.id=idstr;
  with(mSpan.style){
   top=mtop+"px";
   left=mleft+"px";
   background=mcolor;
  }
 };

</script>
</html>

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


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