【转载】HTML5做一个数独游戏的过程详解

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

【转载】HTML5做一个数独游戏的过程详解_第1张图片

 

先简单的了解下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.     
  2.         1
  3.     
  •     
  •         2
  •     
  •     
  •         3
  •     
  •     
  •         4
  •     
  •     
  •         5
  •     
  •     
  •         6
  •     
  •     
  •         7
  •     
  •     
  •         8
  •     
  •     
  •         9
  •     
  •     
  •         10
  •     
  • 现在我们想做一个应用,只有相互有倍数关系的div之间才可用拖放。

     

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

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

     

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

     

    第一步,编写dragStart事件函数

      
      
      
      
      
    1. function handleDragStart(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.         var a = parseInt(e.target.innerHTML);
    11.         var b = 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. function handleDragEnd(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. function handleDragEnter(e) {
    2.     $.log(e);
    3. }
    4. function handleDragOver(e) {
    5.     if (e.preventDefault) {
    6.         e.preventDefault(); //不要执行与事件关联的默认动作
    7.     }
    8.     if (e.stopPropagation) {
    9.         e.stopPropagation(); //停止事件的传播
    10.     }
    11.     $.log(e);
    12.     return false;
    13. }
    14. function handleDragLeave(e) {
    15.     $.log(e);
    16. }
    17. function handleDrop(e) {
    18.     if (e.preventDefault) {
    19.         e.preventDefault(); //不要执行与事件关联的默认动作
    20.     }
    21.     if (e.stopPropagation) {
    22.         e.stopPropagation(); //停止事件的传播
    23.     }
    24.     console.log(e);
    25.     return false;
    26. }

     

     

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

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

    修改后的代码大致如下

      
      
      
      
      
    1. function handleDragStart(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.         var a = parseInt(e.target.innerHTML);
    12.         var b = 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. function handleDragEnd(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. function handleDrop(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.     return false;
    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. [css] view plaincopy
    2. #numberBox > li
    3. {
    4.     width: 30px;
    5.     height: 25px;
    6.     text-align: center;
    7.     font-size: 20px;
    8.     padding-top: 5px;
    9.     float: left;
    10.     color: White;
    11. }

    最后是脚本

      
      
      
      
      
    1. [javascript] view plaincopy
    2.  $(
    3. function() {
    4.     [{ number: 1, bgcolor: "#C71585" }, { number: 2, bgcolor: "#800080" }, { number: 3, bgcolor: "#B8860B" },
    5.     { number: 4, bgcolor: "rgb(0,0,128)" }, { number: 5, bgcolor: "rgb(30,144,255)" },
    6.     { number: 6, bgcolor: "rgb(255,165,0)" },
    7.      { number: 7, bgcolor: "hsl(0,75%,50%)" }, { number: 8, bgcolor: "hsl(30,50%,50%)" },
    8.      { number: 9, bgcolor: "hsl(120,75%,38%)"}].forEach(
    9.      function(key, index) {
    10.      $.log(key);
    11.          var li = $("
    12. ").html(key.number).css("backgroundColor", key.bgcolor).attr("draggable","true");
    13.          $.log(li);
    14.          li[0].addEventListener("dragstart", function() {
    15.          }, false);
    16.          $("#numberBox").append(li);
    17.      }
    18.      );
    19. }
    20. );

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

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

    首先还是htmlTag

      
      
      
      
      
    1. [html] view plaincopy

    然后是样式设定

      
      
      
      
      
    1. [css] view plaincopy
    2. #player .default
    3. {
    4.     float: left;
    5.     width: 48px;
    6.     height: 33px;
    7.     border: solid 1px rgb(0,0,0);
    8.     font-size: 20px;
    9.     padding-top: 15px;
    10.     text-align: center;
    11.     background-color: #B8860B;
    12. }
    13. #player .fix
    14. {
    15.     float: left;
    16.     width: 48px;
    17.     height: 33px;
    18.     border: solid 1px rgb(0,0,0);
    19.     font-size: 20px;
    20.     padding-top: 15px;
    21.     text-align: center;
    22.     background-color: #FFFABC;
    23. }
    24. #player .ation
    25. {
    26.     float: left;
    27.     width: 48px;
    28.     height: 33px;
    29.     border: solid 1px rgb(0,0,0);
    30.     font-size: 20px;
    31.     padding-top: 15px;
    32.     text-align: center;
    33.     background-color: #FFA07A;
    34. }

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

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

      
      
      
      
      
    1. [javascript] view plaincopy
    2.  $(
    3. function() {
    4.     "500000300090500400004000700051037289302080604008052137035000900609000823080023006".split("").forEach(
    5.     function(item, index) {
    6.         $.log(item);
    7.         var li = $("
    8. ")
    9.         if (item != "0") {
    10.             li.addClass("fix");
    11.             li[0].innerHTML = item;
    12.         }
    13.         else {
    14.             li[0].classList.add("default");
    15.             li[0].addEventListener("dragenter",
    16.             function(e) {
    17.                 $.log(e);
    18.             }, false);
    19.             li[0].addEventListener("dragover",
    20.             function(e) {
    21.                 if (e.preventDefault) {
    22.                     e.preventDefault(); //不要执行与事件关联的默认动作
    23.                 }
    24.                 if (e.stopPropagation) {
    25.                     e.stopPropagation(); //停止事件的传播
    26.                 }
    27.                 $.log(e);
    28.                 return false;
    29.             }, false);
    30.             li[0].addEventListener("dragleave",
    31.             function(e) {
    32.             }, false);
    33.             li[0].addEventListener("drop",
    34.             function(e) {
    35.                 if (e.preventDefault) {
    36.                     e.preventDefault(); //不要执行与事件关联的默认动作
    37.                 }
    38.                 if (e.stopPropagation) {
    39.                     e.stopPropagation(); //停止事件的传播
    40.                 }
    41.             }, false);
    42.         }
    43.         $("#player").append(li);
    44.     }
    45.     );
    46. }
    47. );

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

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

      
      
      
      
      
    1. [javascript] view plaincopy
    2.  $(
    3. function() {
    4.     [{ number: 1, bgcolor: "#C71585" }, { number: 2, bgcolor: "#800080" }, { number: 3, bgcolor: "#B8860B" },
    5.     { number: 4, bgcolor: "rgb(0,0,128)" }, { number: 5, bgcolor: "rgb(30,144,255)" },
    6.     { number: 6, bgcolor: "rgb(255,165,0)" },
    7.      { number: 7, bgcolor: "hsl(0,75%,50%)" }, { number: 8, bgcolor: "hsl(30,50%,50%)" },
    8.      { number: 9, bgcolor: "hsl(120,75%,38%)"}].forEach(
    9.      function(key, index) {
    10.          //$.log(key);
    11.          var li = $("
    12. ").html(key.number).css("backgroundColor", key.bgcolor).attr("draggable", "true");
    13.          //$.log(li);
    14.          li[0].addEventListener("dragstart", function(e) {
    15.              e.dataTransfer.effectAllowed = "copyMove";
    16.              e.dataTransfer.setData("text/plain", this.innerHTML);
    17.              $.log(this.innerHTML);
    18.              [ ].forEach.call(document.querySelectorAll("#player .default"),
    19.          function(item) {
    20.              //$.log(item);
    21.              item.classList.remove("default");
    22.              item.classList.add("ation");
    23.          });
    24.          }, false);
    25.          li[0].addEventListener("dragend", function() {
    26.              [ ].forEach.call(document.querySelectorAll("#player .ation"),
    27.          function(item) {
    28.              item.classList.remove("ation");
    29.              item.classList.add("default");
    30.          });
    31.          }, false);
    32.          $("#numberBox").append(li);
    33.      }
    34.      );
    35. }
    36. );

     

     

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

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

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

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

    转自 http://shyleoking.blog.51cto.com/1374881/863969#559183-tsina-1-13805-ed0973a0c870156ed15f06a6573c8bf0

    你可能感兴趣的:(HTML,5技术概览,HTML5,技术博文,HTML5前沿技术,HTML5学习资源)