天天看點

使用HTML5的drag&drop做一個數獨遊戲

 數獨是很好玩的遊戲,之前我用jQuery做了一個數獨遊戲,因為用javaScript來實作drag和drap非常麻煩,jQuery的UI提供了一套非常不錯的drag和drap(以後就簡稱DnD算了),友善我們開發。現在HTML5支援原生的DnD了,那我們來學習下,并且将原先的數獨遊戲遷移到HTML5的DnD應用來。

<a href="http://blog.51cto.com/attachment/201205/161439507.gif" target="_blank"></a>

先簡單的了解下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元素,并且都标記允許拖放

&lt;body&gt; 

    &lt;div style="width: 50px; height: 50px; background-color: Red;" draggable="true"&gt; 

        1 

    &lt;/div&gt; 

    &lt;div style="width: 50px; height: 50px; background-color: Yellow;" draggable="true"&gt; 

        2 

    &lt;div style="width: 50px; height: 50px; background-color: Blue;" draggable="true"&gt; 

        3 

    &lt;div style="width: 50px; height: 50px; background-color: Lime;" draggable="true"&gt; 

        4 

    &lt;div style="width: 50px; height: 50px; background-color: Maroon;" draggable="true"&gt; 

        5 

    &lt;div style="width: 50px; height: 50px; background-color: Black;" draggable="true"&gt; 

        6 

    &lt;div style="width: 50px; height: 50px; background-color: Orange;" draggable="true"&gt; 

        7 

    &lt;div style="width: 50px; height: 50px; background-color: Olive;" draggable="true"&gt; 

        8 

    &lt;div style="width: 50px; height: 50px; background-color: Teal;" draggable="true"&gt; 

        9 

    &lt;div style="width: 50px; height: 50px; background-color: Green;" draggable="true"&gt; 

        10 

&lt;/body&gt; 

現在我們想做一個應用,隻有互相有倍數關系的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 &amp;&amp; b % a != 0) { 

            item.style.opacity = "0.1"; 

        } 

    }); 

以上的代碼有幾個要點

1 對事件來講this、e.target和e.srcElement都是同一對象

2 在forEach中,this是指item,是以forEach中,我們要用e.target來引用

但是一測試我們就發現雖然元素可以拖拉,但并沒有事件激活,那是應為我們沒有為元素綁定事件,是以現在我們用addEventListener來将元素和事件綁定

$( 

function() { 

function(item) { 

     item.addEventListener("dragstart", handleDragStart, false);  

); 

現在我們可以看到,當任意元素拖動的時候,不和其元素有互相倍數的元素變了很淡了。

第二步,當我們拖放完成後,所有div恢複原先顔色,那自然是編寫handleDragEnd

function handleDragEnd(e) { 

    if (e.preventDefault) { 

        e.preventDefault(); //不要執行與事件關聯的預設動作 

    } 

        item.style.opacity = "1"; 

    ); 

記得将上面的事件做綁定哦,應該類似以下代碼

     item.addEventListener("dragstart", handleDragStart, false); 

     item.addEventListener("dragend", handleDragEnd, false);  

第三步,我們要通知那些互為倍數的元素允許我們做拖入操作

我們先需要為目标元素定義些事件函數

function handleDragEnter(e) { 

    $.log(e); 

function handleDragOver(e) { 

    if (e.stopPropagation) { 

        e.stopPropagation(); //停止事件的傳播 

    return false; 

function handleDragLeave(e) { 

function handleDrop(e) { 

    console.log(e); 

注意我們使用了preventDefault和stopPropagation消除了浏覽器預設的一些動作。

顯然這些事件不是所有的元素都有的,隻有互為倍數才有,是以我們要去修改handleDragStart函數了

修改後的代碼大緻如下

        else { 

            item.addEventListener("dragover", handleDragOver, false); 

            item.addEventListener("dragenter", handleDragEnter, false); 

            item.addEventListener("dragleave", handleDragLeave, false); 

            item.addEventListener("drop", handleDrop, false); 

現在你可以發現,當我們拖動互為倍數的元素是,視覺效果明顯是允許拖入,否則就不允許了。當然記得在handleDragEnd函數中要恢複哦

        item.removeEventListener("dragover", handleDragOver, false); 

        item.removeEventListener("dragenter", handleDragEnter, false); 

        item.removeEventListener("dragleave", handleDragLeave, false); 

        item.removeEventListener("drop", handleDrop, false); 

第四步,當我們在可以放置的元素上結束拖放後,我們希望兩個元素的值累計,并出現在被放置的元素裡面,我們需要修改handleDrop函數

    this.innerHTML = parseInt(this.innerHTML)+parseInt(e.dataTransfer.getData("text/plain")) 

好了,到現在為止,準備知識都差不多了,而且作品我們也可以得意的玩玩,你可以發現div被累計都,再次拖拉的時候,是重新計算互相的倍數的,對不?

最後,我感覺黑色的字不好看,我們修改下初始化的函數

    item.addEventListener("dragstart", handleDragStart, false); 

    item.addEventListener("dragend", handleDragEnd, false); 

    item.style.color = "White"; 

好了,現在這個無聊的拖動作品,至少可以打發下你的時間,對不,欣賞下自己的作品吧,接下來,我們開始做正式做數獨遊戲了。

第一步,我們先生成一個1-9的數字對象矩形,這個矩形可以拖動。

先設計htmltag

&lt;ol id="numberBox" style="list-style-type: none; width: 90px; height: 90px;"&gt; 

&lt;/ol&gt; 

然後準備些樣式

[css] view plaincopy 

#numberBox &gt; li   

{   

    width: 30px;   

    height: 25px;   

    text-align: center;   

    font-size: 20px;   

    padding-top: 5px;   

    float: left;   

    color: White;   

}   

最後是腳本

[javascript] view plaincopy 

 $(   

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 = $("&lt;li&gt;").html(key.number).css("backgroundColor", key.bgcolor).attr("draggable","true");   

         $.log(li);   

         li[0].addEventListener("dragstart", function() {   

         }, false);   

         $("#numberBox").append(li);   

     }   

     );   

);   

好,然後你運作下頁面,可以看到一個九宮格,并且每一個格子都可以拖動。

第二步,我們要類似的建立我們填數字得的區域了。

首先還是htmlTag

[html] view plaincopy 

&lt;ol id="player" style="list-style-type: none; width: 450px; height: 450px;"&gt;   

&lt;/ol&gt;   

然後是樣式設定

#player .default   

    width: 48px;   

    height: 33px;   

    border: solid 1px rgb(0,0,0);   

    padding-top: 15px;   

    background-color: #B8860B;   

#player .fix   

    background-color: #FFFABC;   

#player .ation   

    background-color: #FFA07A;   

然後初始化這個區域。數獨填字區域有的格子有值有的沒有值,我用0表示沒有值将來你可以填制,非0表示是固定區不可以改

現在你已經可以在頁面上看到一個非常威武的“獨”字了!

    "500000300090500400004000700051037289302080604008052137035000900609000823080023006".split("").forEach(   

    function(item, index) {   

        $.log(item);   

        var li = $("&lt;li&gt;")   

        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",   

                if (e.preventDefault) {   

                    e.preventDefault(); //不要執行與事件關聯的預設動作   

                }   

                if (e.stopPropagation) {   

                    e.stopPropagation(); //停止事件的傳播   

                return false;   

            li[0].addEventListener("dragleave",   

            li[0].addEventListener("drop",   

        $("#player").append(li);   

    }   

    );   

第三步:我們拖動數字之後,希望可以填數字的區域有明顯的顔色變化并給出提示,同時固定區域不可以拖進去,其他區域可以拖進去,并且拖動的時候要send值。

有了前面的知識,我們馬上知道去哪裡改事件控制了:dragstart事件

    { number: 4, bgcolor: "rgb(0,0,128)" }, { number: 5, bgcolor: "rgb(30,144,255)" },   

     { number: 7, bgcolor: "hsl(0,75%,50%)" }, { number: 8, bgcolor: "hsl(30,50%,50%)" },   

         //$.log(key);   

         var li = $("&lt;li&gt;").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");   

         });   

         li[0].addEventListener("dragend", function() {   

             [ ].forEach.call(document.querySelectorAll("#player .ation"),   

             item.classList.remove("ation");   

             item.classList.add("default");   

現在你可以測試下了,當你拖動數字的時候,有明顯的顔色改變,并且不同的區域你的滑鼠樣式也不同哦。 

第四步,我們接受值,并且判斷這個值是否存在行列沖突,如果沖突就提示,否則改寫

                var sendData = e.dataTransfer.getData("text/plain");   

                //獲得#player&gt;li矩陣數組   

                var matrix = Array.prototype.slice.call(document.querySelectorAll("#player&gt;li"));    

                var currIndex = matrix.indexOf(this); //獲得目前元素的位置   

                var rowIndex = currIndex - currIndex % 9; //行開始的位置   

                var colIndex = currIndex % 9//列開始的位置   

                for (var i = rowIndex; i &lt; rowIndex + 9; i++) {   

                    if (i != currIndex &amp;&amp; matrix[i].innerHTML == sendData) {   

                        alert("對不起行上有資料重複,請小心哦!親");   

                        return;   

                    }   

                for (var i = colIndex; i &lt; 81; ii = i + 9) {   

                        alert("對不起列上有資料重複,請小心哦!親");   

                this.innerHTML = sendData;   

現在你可以開始玩啦,雖然顔色不怎麼好看,但至少可以玩,對不,我們第一個html5的實用遊戲。我後期的blog打算再做些類似新浪微網誌的“你畫我猜”還有“接龍遊戲”。

附件是完整的代碼,你要懶的話,就下載下傳吧

<a href="http://down.51cto.com/data/2360575" target="_blank">附件:http://down.51cto.com/data/2360575</a>

本文轉自shyleoking 51CTO部落格,原文連結:http://blog.51cto.com/shyleoking/863969

繼續閱讀