zoukankan      html  css  js  c++  java
  • 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="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>
    View Code

      在线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>
    View Code

      这个可是好东西啊, 有想要的么

       话说poppin越跳越怪了,shit, 怎莫办啊,打开

    作者: NONO
    出处:http://www.cnblogs.com/diligenceday/
    QQ:287101329 

  • 相关阅读:
    tc: Linux HTTP Outgoing Traffic Shaping (Port 80 Traffic Shaping)(转)
    Linux TC的ifb原理以及ingress流控-转
    插件+组件+空间
    Q查询条件
    django中的分页标签
    QuerySet
    url
    view
    HttpReponse
    装饰器
  • 原文地址:https://www.cnblogs.com/diligenceday/p/4591985.html
Copyright © 2011-2022 走看看