`
bcyy
  • 浏览: 1823894 次
文章分类
社区版块
存档分类
最新评论

使用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元素,并且都标记允许拖放

  1. <body>
  2. <divstyle="width:50px;height:50px;background-color:Red;"draggable="true">
  3. 1
  4. </div>
  5. <divstyle="width:50px;height:50px;background-color:Yellow;"draggable="true">
  6. 2
  7. </div>
  8. <divstyle="width:50px;height:50px;background-color:Blue;"draggable="true">
  9. 3
  10. </div>
  11. <divstyle="width:50px;height:50px;background-color:Lime;"draggable="true">
  12. 4
  13. </div>
  14. <divstyle="width:50px;height:50px;background-color:Maroon;"draggable="true">
  15. 5
  16. </div>
  17. <divstyle="width:50px;height:50px;background-color:Black;"draggable="true">
  18. 6
  19. </div>
  20. <divstyle="width:50px;height:50px;background-color:Orange;"draggable="true">
  21. 7
  22. </div>
  23. <divstyle="width:50px;height:50px;background-color:Olive;"draggable="true">
  24. 8
  25. </div>
  26. <divstyle="width:50px;height:50px;background-color:Teal;"draggable="true">
  27. 9
  28. </div>
  29. <divstyle="width:50px;height:50px;background-color:Green;"draggable="true">
  30. 10
  31. </div>
  32. </body>
现在我们想做一个应用,只有相互有倍数关系的div之间才可用拖放。

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

  1. $.log=function(msg){
  2. console.log(msg);
  3. }
这个我们可以方便的$.log()输出,而不要写冗长的console.log了

第一步,编写dragStart事件函数

  1. functionhandleDragStart(e){
  2. this.style.opacity="0.5";
  3. e.dataTransfer.effectAllowed="move";
  4. e.dataTransfer.setData("text/plain",this.innerHTML);
  5. //$.log(this.innerHTML);
  6. //$.log(e.target.innerHTML);
  7. //$.log(e.srcElement.innerHTML);
  8. [].forEach.call(document.querySelectorAll("div"),
  9. function(item){
  10. vara=parseInt(e.target.innerHTML);
  11. varb=parseInt(item.innerHTML);
  12. if(a%b!=0&&b%a!=0){
  13. item.style.opacity="0.1";
  14. }
  15. });
  16. }
以上的代码有几个要点

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

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



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

  1. $(
  2. function(){
  3. [].forEach.call(document.querySelectorAll("div"),
  4. function(item){
  5. item.addEventListener("dragstart",handleDragStart,false);
  6. }
  7. );
  8. }
  9. );
现在我们可以看到,当任意元素拖动的时候,不和其元素有相互倍数的元素变了很淡了。

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

  1. functionhandleDragEnd(e){
  2. if(e.preventDefault){
  3. e.preventDefault();//不要执行与事件关联的默认动作
  4. }
  5. [].forEach.call(document.querySelectorAll("div"),
  6. function(item){
  7. item.style.opacity="1";
  8. }
  9. );
  10. }
记得将上面的事件做绑定哦,应该类似以下代码
  1. $(
  2. function(){
  3. [].forEach.call(document.querySelectorAll("div"),
  4. function(item){
  5. item.addEventListener("dragstart",handleDragStart,false);
  6. item.addEventListener("dragend",handleDragEnd,false);
  7. }
  8. );
  9. }
  10. );
第三步,我们要通知那些互为倍数的元素允许我们做拖入操作

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

  1. functionhandleDragEnter(e){
  2. $.log(e);
  3. }
  4. functionhandleDragOver(e){
  5. if(e.preventDefault){
  6. e.preventDefault();//不要执行与事件关联的默认动作
  7. }
  8. if(e.stopPropagation){
  9. e.stopPropagation();//停止事件的传播
  10. }
  11. $.log(e);
  12. returnfalse;
  13. }
  14. functionhandleDragLeave(e){
  15. $.log(e);
  16. }
  17. functionhandleDrop(e){
  18. if(e.preventDefault){
  19. e.preventDefault();//不要执行与事件关联的默认动作
  20. }
  21. if(e.stopPropagation){
  22. e.stopPropagation();//停止事件的传播
  23. }
  24. console.log(e);
  25. returnfalse;
  26. }

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

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

修改后的代码大致如下

  1. functionhandleDragStart(e){
  2. this.style.opacity="0.5";
  3. e.dataTransfer.effectAllowed="move";
  4. e.dataTransfer.setData("text/plain",this.innerHTML);
  5. //$.log(this.innerHTML);
  6. //$.log(e.target.innerHTML);
  7. //$.log(e.srcElement.innerHTML);
  8. //$.log(this.innerHTML);
  9. [].forEach.call(document.querySelectorAll("div"),
  10. function(item){
  11. vara=parseInt(e.target.innerHTML);
  12. varb=parseInt(item.innerHTML);
  13. if(a%b!=0&&b%a!=0){
  14. item.style.opacity="0.1";
  15. }
  16. else{
  17. item.addEventListener("dragover",handleDragOver,false);
  18. item.addEventListener("dragenter",handleDragEnter,false);
  19. item.addEventListener("dragleave",handleDragLeave,false);
  20. item.addEventListener("drop",handleDrop,false);
  21. }
  22. });
  23. }
现在你可以发现,当我们拖动互为倍数的元素是,视觉效果明显是允许拖入,否则就不允许了。当然记得在handleDragEnd函数中要恢复哦
  1. functionhandleDragEnd(e){
  2. if(e.preventDefault){
  3. e.preventDefault();//不要执行与事件关联的默认动作
  4. }
  5. [].forEach.call(document.querySelectorAll("div"),
  6. function(item){
  7. item.style.opacity="1";
  8. item.removeEventListener("dragover",handleDragOver,false);
  9. item.removeEventListener("dragenter",handleDragEnter,false);
  10. item.removeEventListener("dragleave",handleDragLeave,false);
  11. item.removeEventListener("drop",handleDrop,false);
  12. }
  13. );
  14. }
第四步,当我们在可以放置的元素上结束拖放后,我们希望两个元素的值累计,并出现在被放置的元素里面,我们需要修改handleDrop函数
  1. functionhandleDrop(e){
  2. if(e.preventDefault){
  3. e.preventDefault();//不要执行与事件关联的默认动作
  4. }
  5. if(e.stopPropagation){
  6. e.stopPropagation();//停止事件的传播
  7. }
  8. this.innerHTML=parseInt(this.innerHTML)+parseInt(e.dataTransfer.getData("text/plain"))
  9. console.log(e);
  10. returnfalse;
  11. }
好了,到现在为止,准备知识都差不多了,而且作品我们也可以得意的玩玩,你可以发现div被累计都,再次拖拉的时候,是重新计算相互的倍数的,对不?

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

  1. $(
  2. function(){
  3. [].forEach.call(document.querySelectorAll("div"),
  4. function(item){
  5. item.addEventListener("dragstart",handleDragStart,false);
  6. item.addEventListener("dragend",handleDragEnd,false);
  7. item.style.color="White";
  8. }
  9. );
  10. }
  11. );


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

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

先设计htmltag

  1. <olid="numberBox"style="list-style-type:none;width:90px;height:90px;">
  2. </ol>
然后准备些样式
  1. #numberBox>li
  2. {
  3. width:30px;
  4. height:25px;
  5. text-align:center;
  6. font-size:20px;
  7. padding-top:5px;
  8. float:left;
  9. color:White;
  10. }
最后是脚本
  1. $(
  2. function(){
  3. [{number:1,bgcolor:"#C71585"},{number:2,bgcolor:"#800080"},{number:3,bgcolor:"#B8860B"},
  4. {number:4,bgcolor:"rgb(0,0,128)"},{number:5,bgcolor:"rgb(30,144,255)"},
  5. {number:6,bgcolor:"rgb(255,165,0)"},
  6. {number:7,bgcolor:"hsl(0,75%,50%)"},{number:8,bgcolor:"hsl(30,50%,50%)"},
  7. {number:9,bgcolor:"hsl(120,75%,38%)"}].forEach(
  8. function(key,index){
  9. $.log(key);
  10. varli=$("<li>").html(key.number).css("backgroundColor",key.bgcolor).attr("draggable","true");
  11. $.log(li);
  12. li[0].addEventListener("dragstart",function(){
  13. },false);
  14. $("#numberBox").append(li);
  15. }
  16. );
  17. }
  18. );

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


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

首先还是htmlTag

  1. <olid="player"style="list-style-type:none;width:450px;height:450px;">
  2. </ol>
然后是样式设定
  1. #player.default
  2. {
  3. float:left;
  4. width:48px;
  5. height:33px;
  6. border:solid1pxrgb(0,0,0);
  7. font-size:20px;
  8. padding-top:15px;
  9. text-align:center;
  10. background-color:#B8860B;
  11. }
  12. #player.fix
  13. {
  14. float:left;
  15. width:48px;
  16. height:33px;
  17. border:solid1pxrgb(0,0,0);
  18. font-size:20px;
  19. padding-top:15px;
  20. text-align:center;
  21. background-color:#FFFABC;
  22. }
  23. #player.ation
  24. {
  25. float:left;
  26. width:48px;
  27. height:33px;
  28. border:solid1pxrgb(0,0,0);
  29. font-size:20px;
  30. padding-top:15px;
  31. text-align:center;
  32. background-color:#FFA07A;
  33. }
然后初始化这个区域。数独填字区域有的格子有值有的没有值,我用0表示没有值将来你可以填制,非0表示是固定区不可以改
  1. $(
  2. function(){
  3. "500000300090500400004000700051037289302080604008052137035000900609000823080023006".split("").forEach(
  4. function(item,index){
  5. $.log(item);
  6. varli=$("<li>")
  7. if(item!="0"){
  8. li.addClass("fix");
  9. li[0].innerHTML=item;
  10. }
  11. else{
  12. li[0].classList.add("default");
  13. li[0].addEventListener("dragenter",
  14. function(e){
  15. $.log(e);
  16. },false);
  17. li[0].addEventListener("dragover",
  18. function(e){
  19. if(e.preventDefault){
  20. e.preventDefault();//不要执行与事件关联的默认动作
  21. }
  22. if(e.stopPropagation){
  23. e.stopPropagation();//停止事件的传播
  24. }
  25. $.log(e);
  26. returnfalse;
  27. },false);
  28. li[0].addEventListener("dragleave",
  29. function(e){
  30. },false);
  31. li[0].addEventListener("drop",
  32. function(e){
  33. if(e.preventDefault){
  34. e.preventDefault();//不要执行与事件关联的默认动作
  35. }
  36. if(e.stopPropagation){
  37. e.stopPropagation();//停止事件的传播
  38. }
  39. },false);
  40. }
  41. $("#player").append(li);
  42. }
  43. );
  44. }
  45. );
现在你已经可以在页面上看到一个非常威武的“独”字了!


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

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

  1. $(
  2. function(){
  3. [{number:1,bgcolor:"#C71585"},{number:2,bgcolor:"#800080"},{number:3,bgcolor:"#B8860B"},
  4. {number:4,bgcolor:"rgb(0,0,128)"},{number:5,bgcolor:"rgb(30,144,255)"},
  5. {number:6,bgcolor:"rgb(255,165,0)"},
  6. {number:7,bgcolor:"hsl(0,75%,50%)"},{number:8,bgcolor:"hsl(30,50%,50%)"},
  7. {number:9,bgcolor:"hsl(120,75%,38%)"}].forEach(
  8. function(key,index){
  9. //$.log(key);
  10. varli=$("<li>").html(key.number).css("backgroundColor",key.bgcolor).attr("draggable","true");
  11. //$.log(li);
  12. li[0].addEventListener("dragstart",function(e){
  13. e.dataTransfer.effectAllowed="copyMove";
  14. e.dataTransfer.setData("text/plain",this.innerHTML);
  15. $.log(this.innerHTML);
  16. [].forEach.call(document.querySelectorAll("#player.default"),
  17. function(item){
  18. //$.log(item);
  19. item.classList.remove("default");
  20. item.classList.add("ation");
  21. });
  22. },false);
  23. li[0].addEventListener("dragend",function(){
  24. [].forEach.call(document.querySelectorAll("#player.ation"),
  25. function(item){
  26. item.classList.remove("ation");
  27. item.classList.add("default");
  28. });
  29. },false);
  30. $("#numberBox").append(li);
  31. }
  32. );
  33. }
  34. );
现在你可以测试下了,当你拖动数字的时候,有明显的颜色改变,并且不同的区域你的鼠标样式也不同哦。

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

  1. $(
  2. function(){
  3. "500000300090500400004000700051037289302080604008052137035000900609000823080023006".split("").forEach(
  4. function(item,index){
  5. $.log(item);
  6. varli=$("<li>")
  7. if(item!="0"){
  8. li.addClass("fix");
  9. li[0].innerHTML=item;
  10. }
  11. else{
  12. li[0].classList.add("default");
  13. li[0].addEventListener("dragenter",
  14. function(e){
  15. $.log(e);
  16. },false);
  17. li[0].addEventListener("dragover",
  18. function(e){
  19. if(e.preventDefault){
  20. e.preventDefault();//不要执行与事件关联的默认动作
  21. }
  22. if(e.stopPropagation){
  23. e.stopPropagation();//停止事件的传播
  24. }
  25. $.log(e);
  26. returnfalse;
  27. },false);
  28. li[0].addEventListener("dragleave",
  29. function(e){
  30. },false);
  31. li[0].addEventListener("drop",
  32. function(e){
  33. if(e.preventDefault){
  34. e.preventDefault();//不要执行与事件关联的默认动作
  35. }
  36. if(e.stopPropagation){
  37. e.stopPropagation();//停止事件的传播
  38. }
  39. varsendData=e.dataTransfer.getData("text/plain");
  40. //获得#player>li矩阵数组
  41. varmatrix=Array.prototype.slice.call(document.querySelectorAll("#player>li"));
  42. varcurrIndex=matrix.indexOf(this);//获得当前元素的位置
  43. varrowIndex=currIndex-currIndex%9;//行开始的位置
  44. varcolIndex=currIndex%9//列开始的位置
  45. for(vari=rowIndex;i<rowIndex+9;i++){
  46. if(i!=currIndex&&matrix[i].innerHTML==sendData){
  47. alert("对不起行上有数据重复,请小心哦!亲");
  48. return;
  49. }
  50. }
  51. for(vari=colIndex;i<81;i=i+9){
  52. if(i!=currIndex&&matrix[i].innerHTML==sendData){
  53. alert("对不起列上有数据重复,请小心哦!亲");
  54. return;
  55. }
  56. }
  57. this.innerHTML=sendData;
  58. },false);
  59. }
  60. $("#player").append(li);
  61. }
  62. );
  63. }
  64. );


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

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

  1. <!DOCTYPEhtmlPUBLIC"-//W3C//DTDXHTML1.0Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  2. <htmlxmlns="http://www.w3.org/1999/xhtml">
  3. <head>
  4. <title></title>
  5. <scriptsrc=../../"js/jquery-1.7.1.min.js"type="text/javascript"></script>
  6. <scripttype="text/javascript">
  7. $.log=function(msg){
  8. console.log(msg);
  9. }
  10. $(
  11. function(){
  12. [{number:1,bgcolor:"#C71585"},{number:2,bgcolor:"#800080"},{number:3,bgcolor:"#B8860B"},
  13. {number:4,bgcolor:"rgb(0,0,128)"},{number:5,bgcolor:"rgb(30,144,255)"},
  14. {number:6,bgcolor:"rgb(255,165,0)"},
  15. {number:7,bgcolor:"hsl(0,75%,50%)"},{number:8,bgcolor:"hsl(30,50%,50%)"},
  16. {number:9,bgcolor:"hsl(120,75%,38%)"}].forEach(
  17. function(key,index){
  18. //$.log(key);
  19. varli=$("<li>").html(key.number).css("backgroundColor",key.bgcolor).attr("draggable","true");
  20. //$.log(li);
  21. li[0].addEventListener("dragstart",function(e){
  22. e.dataTransfer.effectAllowed="copyMove";
  23. e.dataTransfer.setData("text/plain",this.innerHTML);
  24. $.log(this.innerHTML);
  25. [].forEach.call(document.querySelectorAll("#player.default"),
  26. function(item){
  27. //$.log(item);
  28. item.classList.remove("default");
  29. item.classList.add("ation");
  30. });
  31. },false);
  32. li[0].addEventListener("dragend",function(){
  33. [].forEach.call(document.querySelectorAll("#player.ation"),
  34. function(item){
  35. item.classList.remove("ation");
  36. item.classList.add("default");
  37. });
  38. },false);
  39. $("#numberBox").append(li);
  40. }
  41. );
  42. }
  43. );
  44. $(
  45. function(){
  46. "500000300090500400004000700051037289302080604008052137035000900609000823080023006".split("").forEach(
  47. function(item,index){
  48. $.log(item);
  49. varli=$("<li>")
  50. if(item!="0"){
  51. li.addClass("fix");
  52. li[0].innerHTML=item;
  53. }
  54. else{
  55. li[0].classList.add("default");
  56. li[0].addEventListener("dragenter",
  57. function(e){
  58. $.log(e);
  59. },false);
  60. li[0].addEventListener("dragover",
  61. function(e){
  62. if(e.preventDefault){
  63. e.preventDefault();//不要执行与事件关联的默认动作
  64. }
  65. if(e.stopPropagation){
  66. e.stopPropagation();//停止事件的传播
  67. }
  68. $.log(e);
  69. returnfalse;
  70. },false);
  71. li[0].addEventListener("dragleave",
  72. function(e){
  73. },false);
  74. li[0].addEventListener("drop",
  75. function(e){
  76. if(e.preventDefault){
  77. e.preventDefault();//不要执行与事件关联的默认动作
  78. }
  79. if(e.stopPropagation){
  80. e.stopPropagation();//停止事件的传播
  81. }
  82. varsendData=e.dataTransfer.getData("text/plain");
  83. //获得#player>li矩阵数组
  84. varmatrix=Array.prototype.slice.call(document.querySelectorAll("#player>li"));
  85. varcurrIndex=matrix.indexOf(this);//获得当前元素的位置
  86. varrowIndex=currIndex-currIndex%9;//行开始的位置
  87. varcolIndex=currIndex%9//列开始的位置
  88. for(vari=rowIndex;i<rowIndex+9;i++){
  89. if(i!=currIndex&&matrix[i].innerHTML==sendData){
  90. alert("对不起行上有数据重复,请小心哦!亲");
  91. return;
  92. }
  93. }
  94. for(vari=colIndex;i<81;i=i+9){
  95. if(i!=currIndex&&matrix[i].innerHTML==sendData){
  96. alert("对不起列上有数据重复,请小心哦!亲");
  97. return;
  98. }
  99. }
  100. this.innerHTML=sendData;
  101. },false);
  102. }
  103. $("#player").append(li);
  104. }
  105. );
  106. }
  107. );
  108. </script>
  109. <styletype="text/css">
  110. #numberBox>li
  111. {
  112. width:30px;
  113. height:25px;
  114. text-align:center;
  115. font-size:20px;
  116. padding-top:5px;
  117. float:left;
  118. color:White;
  119. }
  120. #player.default
  121. {
  122. float:left;
  123. width:48px;
  124. height:33px;
  125. border:solid1pxrgb(0,0,0);
  126. font-size:20px;
  127. padding-top:15px;
  128. text-align:center;
  129. background-color:#B8860B;
  130. }
  131. #player.fix
  132. {
  133. float:left;
  134. width:48px;
  135. height:33px;
  136. border:solid1pxrgb(0,0,0);
  137. font-size:20px;
  138. padding-top:15px;
  139. text-align:center;
  140. background-color:#FFFABC;
  141. }
  142. #player.ation
  143. {
  144. float:left;
  145. width:48px;
  146. height:33px;
  147. border:solid1pxrgb(0,0,0);
  148. font-size:20px;
  149. padding-top:15px;
  150. text-align:center;
  151. background-color:#FFA07A;
  152. }
  153. </style>
  154. </head>
  155. <body>
  156. <olid="numberBox"style="list-style-type:none;width:90px;height:90px;">
  157. </ol>
  158. <olid="player"style="list-style-type:none;width:450px;height:450px;">
  159. </ol>
  160. </body>

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics