使用HTML5的drag&drop做一个数独游戏


数独是很好玩的游戏,之前我用jQuery做了一个数独游戏,因为用javaScript来实现drag和drap非常麻烦,jQuery的UI提供了一套非常不错的drag和drap(以后就简称DnD算了),方便我们开发。现在HTML5支持原生的DnD了,那我们来学习下,并且将原先的数独游戏迁移到HTML5的DnD应用来。




先简单的了解下HTML5的DnD事件模型,事件发生在源元素(被拖动的元素)和目标元素(被进入的元素)上,为了简单的描述,我们将源元素称为src,目标元素叫des。

drag:src[拖动中] dragstart:src[开始拖动]
dragenter:des[进入目标]
dragover:des[在目标移动]
dragleave:des[离开目标]
drop:des[释放拖动]
dragend:src[拖动完成]
所有的事件我们知道肯定都应该给我们一个event对象,帮助我们获得一些信息或我们来设置一些信息,以上事件都可以得到一个event,如果我们的事件函数是function(e)那

e.dataTransfer.effectAllowed,只能在dragstart事件设置,值为以下之一:"none", "copy", "copyLink", "copyMove", "link", "linkMove", "move", "all", and "uninitialized"

e.dataTransfer.dropEffect,返回拖来的行为,对应上面的effectAllowed,值是:"none", "copy", "link", and "move"

e.target,可以得到当前事件的dom对象,比如你可以得到e.target.innerHTML,或者设置e.target.classList.add,或者e.target.classList.remove

e.dataTransfer.setData(foramt,value),为拖动赋值,foramt的值是为了描述值的类型,一般有text/plain 和 text/uri-list

e.dataTransfer.getData(foramt),获取被拖来的元素通过setData存储的值

e.stopPropagation,阻止事件冒泡,这样可以防止子元素的拖动处理被带到父元素事件中(触发父元素事件),在IE中可以用e.cancelBubble = true

e.preventDefault,阻止默认事件发生,也可以简单的写return false,在IE中可以用e.returnValue = false

有了上面的基本概念,我们先做一个小小的模型,来测试几个技术要点:监视拖放事件,改变元素在拖放中的样式,传递值和检查值什么的

在body里面,我们声明了10个div元素,并且都标记允许拖放

<body>    <div style="width: 50px; height: 50px; background-color: Red;" draggable="true">
1
</div>
<div style="width: 50px; height: 50px; background-color: Yellow;" draggable="true">
2
</div>
<div style="width: 50px; height: 50px; background-color: Blue;" draggable="true">
3
</div>
<div style="width: 50px; height: 50px; background-color: Lime;" draggable="true">
4
</div>
<div style="width: 50px; height: 50px; background-color: Maroon;" draggable="true">
5
</div>
<div style="width: 50px; height: 50px; background-color: Black;" draggable="true">
6
</div>
<div style="width: 50px; height: 50px; background-color: Orange;" draggable="true">
7
</div>
<div style="width: 50px; height: 50px; background-color: Olive;" draggable="true">
8
</div>
<div style="width: 50px; height: 50px; background-color: Teal;" draggable="true">
9
</div>
<div style="width: 50px; height: 50px; background-color: Green;" draggable="true">
10
</div>
</body>

现在我们想做一个应用,只有相互有倍数关系的div之间才可用拖放。

首选我们做一个用于输出调式的小工具代码

$.log = function(msg) {    console.log(msg);}

这个我们可以方便的$.log()输出,而不要写冗长的console.log了

第一步,编写dragStart事件函数

function handleDragStart(e) {    this.style.opacity = "0.5";    e.dataTransfer.effectAllowed = "move";    e.dataTransfer.setData("text/plain", this.innerHTML);    //$.log(this.innerHTML);    //$.log(e.target.innerHTML);    //$.log(e.srcElement.innerHTML);    [ ].forEach.call(document.querySelectorAll("div"),    function(item) {        var a = parseInt(e.target.innerHTML);        var b = parseInt(item.innerHTML);        if (a % b != 0 && b % a != 0) {            item.style.opacity = "0.1";        }    });}

以上的代码有几个要点

1 对事件来讲this、e.target和e.srcElement都是同一对象

2 在forEach中,this是指item,所以forEach中,我们要用e.target来引用


但是一测试我们就发现虽然元素可以拖拉,但并没有事件激活,那是应为我们没有为元素绑定事件,所以现在我们用addEventListener来将元素和事件绑定

$(function() {    [ ].forEach.call(document.querySelectorAll("div"),function(item) {     item.addEventListener("dragstart", handleDragStart, false); });});

现在我们可以看到,当任意元素拖动的时候,不和其元素有相互倍数的元素变了很淡了。

第二步,当我们拖放完成后,所有div恢复原先颜色,那自然是编写handleDragEnd

function handleDragEnd(e) {    if (e.preventDefault) {        e.preventDefault(); //不要执行与事件关联的默认动作    }    [ ].forEach.call(document.querySelectorAll("div"),    function(item) {        item.style.opacity = "1";    }    );}

记得将上面的事件做绑定哦,应该类似以下代码

$(function() {    [ ].forEach.call(document.querySelectorAll("div"),function(item) {     item.addEventListener("dragstart", handleDragStart, false);     item.addEventListener("dragend", handleDragEnd, false); });});

第三步,我们要通知那些互为倍数的元素允许我们做拖入操作

我们先需要为目标元素定义些事件函数

function handleDragEnter(e) {    $.log(e);}function handleDragOver(e) {    if (e.preventDefault) {        e.preventDefault(); //不要执行与事件关联的默认动作    }    if (e.stopPropagation) {        e.stopPropagation(); //停止事件的传播    }    $.log(e);    return false;}function handleDragLeave(e) {    $.log(e);}function handleDrop(e) {    if (e.preventDefault) {        e.preventDefault(); //不要执行与事件关联的默认动作    }    if (e.stopPropagation) {        e.stopPropagation(); //停止事件的传播    }    console.log(e);    return false;}


注意我们使用了preventDefault和stopPropagation消除了浏览器默认的一些动作。

显然这些事件不是所有的元素都有的,只有互为倍数才有,所以我们要去修改handleDragStart函数了

修改后的代码大致如下

function handleDragStart(e) {    this.style.opacity = "0.5";    e.dataTransfer.effectAllowed = "move";    e.dataTransfer.setData("text/plain", this.innerHTML);    //$.log(this.innerHTML);    //$.log(e.target.innerHTML);    //$.log(e.srcElement.innerHTML);    //$.log(this.innerHTML);    [ ].forEach.call(document.querySelectorAll("div"),    function(item) {        var a = parseInt(e.target.innerHTML);        var b = parseInt(item.innerHTML);        if (a % b != 0 && b % a != 0) {            item.style.opacity = "0.1";        }        else {            item.addEventListener("dragover", handleDragOver, false);            item.addEventListener("dragenter", handleDragEnter, false);            item.addEventListener("dragleave", handleDragLeave, false);            item.addEventListener("drop", handleDrop, false);        }    });}

现在你可以发现,当我们拖动互为倍数的元素是,视觉效果明显是允许拖入,否则就不允许了。当然记得在handleDragEnd函数中要恢复哦

function handleDragEnd(e) {    if (e.preventDefault) {        e.preventDefault(); //不要执行与事件关联的默认动作    }    [ ].forEach.call(document.querySelectorAll("div"),    function(item) {        item.style.opacity = "1";        item.removeEventListener("dragover", handleDragOver, false);        item.removeEventListener("dragenter", handleDragEnter, false);        item.removeEventListener("dragleave", handleDragLeave, false);        item.removeEventListener("drop", handleDrop, false);    }    );}

第四步,当我们在可以放置的元素上结束拖放后,我们希望两个元素的值累计,并出现在被放置的元素里面,我们需要修改handleDrop函数

function handleDrop(e) {    if (e.preventDefault) {        e.preventDefault(); //不要执行与事件关联的默认动作    }    if (e.stopPropagation) {        e.stopPropagation(); //停止事件的传播    }    this.innerHTML = parseInt(this.innerHTML)+parseInt(e.dataTransfer.getData("text/plain"))    console.log(e);    return false;}

好了,到现在为止,准备知识都差不多了,而且作品我们也可以得意的玩玩,你可以发现div被累计都,再次拖拉的时候,是重新计算相互的倍数的,对不?

最后,我感觉黑色的字不好看,我们修改下初始化的函数

$(function() {    [ ].forEach.call(document.querySelectorAll("div"),function(item) {    item.addEventListener("dragstart", handleDragStart, false);    item.addEventListener("dragend", handleDragEnd, false);    item.style.color = "White";});});

好了,现在这个无聊的拖动作品,至少可以打发下你的时间,对不,欣赏下自己的作品吧,接下来,我们开始做正式做数独游戏了。

第一步,我们先生成一个1-9的数字对象矩形,这个矩形可以拖动。

先设计htmltag

<ol id="numberBox" style="list-style-type: none; width: 90px; height: 90px;"></ol>

然后准备些样式

#numberBox > li{    width: 30px;    height: 25px;    text-align: center;    font-size: 20px;    padding-top: 5px;    float: left;    color: White;}

最后是脚本

 $(function() {    [{ number: 1, bgcolor: "#C71585" }, { number: 2, bgcolor: "#800080" }, { number: 3, bgcolor: "#B8860B" },    { number: 4, bgcolor: "rgb(0,0,128)" }, { number: 5, bgcolor: "rgb(30,144,255)" },     { number: 6, bgcolor: "rgb(255,165,0)" },     { number: 7, bgcolor: "hsl(0,75%,50%)" }, { number: 8, bgcolor: "hsl(30,50%,50%)" },      { number: 9, bgcolor: "hsl(120,75%,38%)"}].forEach(     function(key, index) {     $.log(key);         var li = $("<li>").html(key.number).css("backgroundColor", key.bgcolor).attr("draggable","true");         $.log(li);         li[0].addEventListener("dragstart", function() {         }, false);         $("#numberBox").append(li);     }     );});

好,然后你运行下页面,可以看到一个九宫格,并且每一个格子都可以拖动。


第二步,我们要类似的创建我们填数字得的区域了。

首先还是htmlTag

<ol id="player" style="list-style-type: none; width: 450px; height: 450px;"></ol>

然后是样式设定

#player .default{    float: left;    width: 48px;    height: 33px;    border: solid 1px rgb(0,0,0);    font-size: 20px;    padding-top: 15px;    text-align: center;    background-color: #B8860B;}#player .fix{    float: left;    width: 48px;    height: 33px;    border: solid 1px rgb(0,0,0);    font-size: 20px;    padding-top: 15px;    text-align: center;    background-color: #FFFABC;}#player .ation{    float: left;    width: 48px;    height: 33px;    border: solid 1px rgb(0,0,0);    font-size: 20px;    padding-top: 15px;    text-align: center;    background-color: #FFA07A;}

然后初始化这个区域。数独填字区域有的格子有值有的没有值,我用0表示没有值将来你可以填制,非0表示是固定区不可以改

 $(function() {    "500000300090500400004000700051037289302080604008052137035000900609000823080023006".split("").forEach(    function(item, index) {        $.log(item);        var li = $("<li>")        if (item != "0") {            li.addClass("fix");            li[0].innerHTML = item;        }        else {            li[0].classList.add("default");            li[0].addEventListener("dragenter",            function(e) {                $.log(e);            }, false);            li[0].addEventListener("dragover",            function(e) {                if (e.preventDefault) {                    e.preventDefault(); //不要执行与事件关联的默认动作                }                if (e.stopPropagation) {                    e.stopPropagation(); //停止事件的传播                }                $.log(e);                return false;            }, false);            li[0].addEventListener("dragleave",            function(e) {            }, false);            li[0].addEventListener("drop",            function(e) {                if (e.preventDefault) {                    e.preventDefault(); //不要执行与事件关联的默认动作                }                if (e.stopPropagation) {                    e.stopPropagation(); //停止事件的传播                }            }, false);        }        $("#player").append(li);    }    );});

现在你已经可以在页面上看到一个非常威武的“独”字了!


第三步:我们拖动数字之后,希望可以填数字的区域有明显的颜色变化并给出提示,同时固定区域不可以拖进去,其他区域可以拖进去,并且拖动的时候要send值。

有了前面的知识,我们马上知道去哪里改事件控制了:dragstart事件

 $(function() {    [{ number: 1, bgcolor: "#C71585" }, { number: 2, bgcolor: "#800080" }, { number: 3, bgcolor: "#B8860B" },    { number: 4, bgcolor: "rgb(0,0,128)" }, { number: 5, bgcolor: "rgb(30,144,255)" },    { number: 6, bgcolor: "rgb(255,165,0)" },     { number: 7, bgcolor: "hsl(0,75%,50%)" }, { number: 8, bgcolor: "hsl(30,50%,50%)" },     { number: 9, bgcolor: "hsl(120,75%,38%)"}].forEach(     function(key, index) {         //$.log(key);         var li = $("<li>").html(key.number).css("backgroundColor", key.bgcolor).attr("draggable", "true");         //$.log(li);         li[0].addEventListener("dragstart", function(e) {             e.dataTransfer.effectAllowed = "copyMove";             e.dataTransfer.setData("text/plain", this.innerHTML);             $.log(this.innerHTML);             [ ].forEach.call(document.querySelectorAll("#player .default"),         function(item) {             //$.log(item);             item.classList.remove("default");             item.classList.add("ation");         });         }, false);         li[0].addEventListener("dragend", function() {             [ ].forEach.call(document.querySelectorAll("#player .ation"),         function(item) {             item.classList.remove("ation");             item.classList.add("default");         });         }, false);         $("#numberBox").append(li);     }     );});

现在你可以测试下了,当你拖动数字的时候,有明显的颜色改变,并且不同的区域你的鼠标样式也不同哦。


第四步,我们接受值,并且判断这个值是否存在行列冲突,如果冲突就提示,否则改写

 $(function() {    "500000300090500400004000700051037289302080604008052137035000900609000823080023006".split("").forEach(    function(item, index) {        $.log(item);        var li = $("<li>")        if (item != "0") {            li.addClass("fix");            li[0].innerHTML = item;        }        else {            li[0].classList.add("default");            li[0].addEventListener("dragenter",            function(e) {                $.log(e);            }, false);            li[0].addEventListener("dragover",            function(e) {                if (e.preventDefault) {                    e.preventDefault(); //不要执行与事件关联的默认动作                }                if (e.stopPropagation) {                    e.stopPropagation(); //停止事件的传播                }                $.log(e);                return false;            }, false);            li[0].addEventListener("dragleave",            function(e) {            }, false);            li[0].addEventListener("drop",            function(e) {                if (e.preventDefault) {                    e.preventDefault(); //不要执行与事件关联的默认动作                }                if (e.stopPropagation) {                    e.stopPropagation(); //停止事件的传播                }                var sendData = e.dataTransfer.getData("text/plain");                //获得#player>li矩阵数组                var matrix = Array.prototype.slice.call(document.querySelectorAll("#player>li"));                 var currIndex = matrix.indexOf(this); //获得当前元素的位置                var rowIndex = currIndex - currIndex % 9; //行开始的位置                var colIndex = currIndex % 9//列开始的位置                for (var i = rowIndex; i < rowIndex + 9; i++) {                    if (i != currIndex && matrix[i].innerHTML == sendData) {                        alert("对不起行上有数据重复,请小心哦!亲");                        return;                    }                }                for (var i = colIndex; i < 81; i = i + 9) {                    if (i != currIndex && matrix[i].innerHTML == sendData) {                        alert("对不起列上有数据重复,请小心哦!亲");                        return;                    }                }                this.innerHTML = sendData;            }, false);        }        $("#player").append(li);    }    );});

现在你可以开始玩啦,虽然颜色不怎么好看,但至少可以玩,对不,我们第一个html5的实用游戏。我后期的blog打算再做些类似新浪微博的“你画我猜”还有“接龙游戏”。

下面是完整的代码,你要懒的话,就直接copy吧


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head>    <title></title>    <script src="js/jquery-1.7.1.min.js" type="text/javascript"></script>    <script type="text/javascript">        $.log = function(msg) {            console.log(msg);        }        $(       function() {           [{ number: 1, bgcolor: "#C71585" }, { number: 2, bgcolor: "#800080" }, { number: 3, bgcolor: "#B8860B" },           { number: 4, bgcolor: "rgb(0,0,128)" }, { number: 5, bgcolor: "rgb(30,144,255)" },           { number: 6, bgcolor: "rgb(255,165,0)" },            { number: 7, bgcolor: "hsl(0,75%,50%)" }, { number: 8, bgcolor: "hsl(30,50%,50%)" },            { number: 9, bgcolor: "hsl(120,75%,38%)"}].forEach(            function(key, index) {                //$.log(key);                var li = $("<li>").html(key.number).css("backgroundColor", key.bgcolor).attr("draggable", "true");                //$.log(li);                li[0].addEventListener("dragstart", function(e) {                    e.dataTransfer.effectAllowed = "copyMove";                    e.dataTransfer.setData("text/plain", this.innerHTML);                    $.log(this.innerHTML);                    [ ].forEach.call(document.querySelectorAll("#player .default"),                function(item) {                    //$.log(item);                    item.classList.remove("default");                    item.classList.add("ation");                });                }, false);                li[0].addEventListener("dragend", function() {                    [ ].forEach.call(document.querySelectorAll("#player .ation"),                function(item) {                    item.classList.remove("ation");                    item.classList.add("default");                });                }, false);                $("#numberBox").append(li);            }            );       }       );        $(       function() {           "500000300090500400004000700051037289302080604008052137035000900609000823080023006".split("").forEach(           function(item, index) {               $.log(item);               var li = $("<li>")               if (item != "0") {                   li.addClass("fix");                   li[0].innerHTML = item;               }               else {                   li[0].classList.add("default");                   li[0].addEventListener("dragenter",                   function(e) {                       $.log(e);                   }, false);                   li[0].addEventListener("dragover",                   function(e) {                       if (e.preventDefault) {                           e.preventDefault(); //不要执行与事件关联的默认动作                       }                       if (e.stopPropagation) {                           e.stopPropagation(); //停止事件的传播                       }                       $.log(e);                       return false;                   }, false);                   li[0].addEventListener("dragleave",                   function(e) {                   }, false);                   li[0].addEventListener("drop",                   function(e) {                       if (e.preventDefault) {                           e.preventDefault(); //不要执行与事件关联的默认动作                       }                       if (e.stopPropagation) {                           e.stopPropagation(); //停止事件的传播                       }                       var sendData = e.dataTransfer.getData("text/plain");                       //获得#player>li矩阵数组                       var matrix = Array.prototype.slice.call(document.querySelectorAll("#player>li"));                        var currIndex = matrix.indexOf(this); //获得当前元素的位置                       var rowIndex = currIndex - currIndex % 9; //行开始的位置                       var colIndex = currIndex % 9//列开始的位置                       for (var i = rowIndex; i < rowIndex + 9; i++) {                           if (i != currIndex && matrix[i].innerHTML == sendData) {                               alert("对不起行上有数据重复,请小心哦!亲");                               return;                           }                       }                       for (var i = colIndex; i < 81; i = i + 9) {                           if (i != currIndex && matrix[i].innerHTML == sendData) {                               alert("对不起列上有数据重复,请小心哦!亲");                               return;                           }                       }                       this.innerHTML = sendData;                   }, false);               }               $("#player").append(li);           }           );       }       );                 </script>    <style type="text/css">        #numberBox > li        {            width: 30px;            height: 25px;            text-align: center;            font-size: 20px;            padding-top: 5px;            float: left;            color: White;        }        #player .default        {            float: left;            width: 48px;            height: 33px;            border: solid 1px rgb(0,0,0);            font-size: 20px;            padding-top: 15px;            text-align: center;            background-color: #B8860B;        }        #player .fix        {            float: left;            width: 48px;            height: 33px;            border: solid 1px rgb(0,0,0);            font-size: 20px;            padding-top: 15px;            text-align: center;            background-color: #FFFABC;        }        #player .ation        {            float: left;            width: 48px;            height: 33px;            border: solid 1px rgb(0,0,0);            font-size: 20px;            padding-top: 15px;            text-align: center;            background-color: #FFA07A;        }    </style></head><body>    <ol id="numberBox" style="list-style-type: none; width: 90px; height: 90px;">    </ol>    <ol id="player" style="list-style-type: none; width: 450px; height: 450px;">    </ol></body></html>

智能推荐

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
© 2014-2019 ITdaan.com 粤ICP备14056181号  

赞助商广告