DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> JavaScript入門知識 >> 關於JavaScript >> JavaScript編寫連連看小游戲
JavaScript編寫連連看小游戲
編輯:關於JavaScript     

天天看到別人玩連連看, 表示沒有認真玩過, 不就把兩個一樣的圖片連接在一起麼, 我自己寫一個都可以呢。

  使用Javascript寫了一個, 托管到github, 在線DEMO地址查看:打開

  最終的效果圖:

  寫連連看之前要先考慮哪些呢?

    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結構:

  分數模塊構造函數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>

以上所述 就是本文的全部內容了,希望大家能夠喜歡。

XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved