1. 程式人生 > 其它 >JavaScript事件處理(三)

JavaScript事件處理(三)

 

上機三 JavaScript事件處理

目的:

  1. 熟練掌握JavaScript事件處理機制

  2. 重點理解面向物件程式設計思想,並構建程式。

要求:

  1. 定義一個按鈕,動態生成DIV,可以生成多個DIV;

  2. 實現DIV的自由拖曳,當和以生成的DIV碰撞時,彈出警告框;

  3. 封裝自由拖曳函式,DIV作為引數傳入

原始碼:

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Third_work</title>
</head>
<style>

        /*
將按鈕固定位置於瀏覽器視窗上方 */ .top{ position: fixed; top: 0px; z-index: 1; box-shadow: 0px 0px 10px gainsboro; width: 1519px; height: 55px; } /* 簡單對按鈕進行裝飾 */ input{ width: 150px; height: 50px; border
-radius: 25px; background-color: ghostwhite; outline: none; } </style> <body> <div class="top"> <input type="button" value="make a new div" onclick="newDiv()"> <input type="button" value="clear all div" onclick="clearDiv()"> </div> <div id="content" style="margin-top: 50px;"> <!-- div容器:div在此處生成 --> </div> </body> </html> <script> //
定義全域性變數,記錄生成div的總個數,生成下一個div的位置 var idCount = 0; var positionXCount = 0; var positionYCount = 100; // 順序生成的div的id如:1,2,3,4,5... function getDivId(){ var DivId = ++idCount; return DivId; } // 隨機生成div的背景顏色 function getBackgroundColor(){ var backgroundColor = "#"; var colorNum = ["0","1","2","3","4","5","6","7","8","9", "A","B","c","D","E","F"]; for(let i = 0;i < 6;i++ ){ backgroundColor += colorNum[Math.round(Math.random()*15)]; } return backgroundColor; } // 設定生成div的寬高和位置 function getDivSize(obj){ var divSize = parseFloat(100+Math.random()*100); obj.style.width = obj.style.height = divSize + "px"; obj.style.left = positionXCount + "px"; obj.style.top = positionYCount + "px"; positionXCount += divSize + 100; if(positionXCount > 1300){ positionXCount = 0; positionYCount += divSize + 100; } } // 移動div並處理判斷碰撞 function moveDiv(obj){ var positionX = 0; var positionY = 0; //設定滑鼠按下事件 obj.onmousedown = function (event){ // 阻止滑鼠開啟連結 event.preventDefault(); var hit=0; // 獲取div容器下生成的所有div var Div = document.getElementById("content").querySelectorAll("div"); // 記錄移動前,div位置 var startPositionX = obj.offsetLeft; var startPositionY = obj.offsetTop positionX = event.clientX - startPositionX; positionY = event.clientY - startPositionY; // 設定滑鼠移動事件 document.onmousemove = function(eve){ // 獲取移動時,div的動態位置 var L = eve.clientX-positionX; var R = parseFloat(L + obj.offsetWidth); var T = eve.clientY-positionY; var B = parseFloat(T + obj.offsetHeight); // 比較所有div的位置,位置重合則碰撞 for(var i = 0;i < idCount;i++) { // 避免與自身比較 if((i + 1) == obj.id)i++; // 獲取與之比較的div的位置 var l = parseFloat(Div[i].offsetLeft); var t = parseFloat(Div[i].offsetTop); var r = parseFloat(Div[i].offsetLeft + Div[i].offsetWidth); var b = parseFloat(Div[i].offsetTop + Div[i].offsetHeight); // 兩兩比較,只有以下4種情況是不發生碰撞的,此外都發生碰撞 if(b<=T || B<=t || r<=L || R<=l){ // 不碰撞則移動 obj.style.left = L + "px"; obj.style.top = T + "px"; } else{ setTimeout(function (){ // 將div移動回碰撞前位置 obj.style.left = startPositionX + "px"; obj.style.top = startPositionY + "px"; // 清除移動事件 document.onmousemove = null; if(hit == 0){ alert("hit");hit=1;} },1); } } } // 設定滑鼠抬起事件 document.onmouseup = function () { // 清除事件操作 document.onmousemove = null; document.onmouseup = null; } } } // 按鈕觸發,在div容器內生成div var newDiv = function(){ var ele = document.createElement("div"); ele.id = getDivId(); ele.style.position = "absolute"; ele.style.backgroundColor = getBackgroundColor(); getDivSize(ele); document.getElementById("content").appendChild(ele); ele.innerHTML = ele.id; moveDiv(ele); } // 重置清空div容器,刪除所有div var clearDiv = function(){ document.getElementById("content").innerHTML = ""; idCount = 0; positionXCount = 0; positionYCount = 100; } </script>

 

截圖:

 

 

 

總結(遇到問題、解決方式、心得體會):

 

div的生成和移動相對簡單,但在我完成自由拖拽div、開始寫判斷div碰撞時,我發現了問題。

為了移動div,我將div的position屬性都設定成了absolute,但也因此生成的div都聚集在瀏覽器視窗左上方互相重疊,雖然可以通過拖拽將其移動到其他位置,但如果碰撞功能部分程式碼完成,點選div開始移動時,就會觸發碰撞,彈出警告框從而阻止了div的移動這無疑是個bug。所以若要實現碰撞功能,必須在生成div時就將每個div分隔開。

由於position:absolute絕對位置,通過設定div的外邊距,即margin屬性,將div分隔的操作失效。我想到可以在生成div時使用靜態位置position:static,這樣生成的div是會自動排成一豎列,並且可用margin屬性。只要在觸發拖拽移動時,將position屬性修改回絕對位置ele.style.position = "absolute";這樣既實現了生成分隔開的div,又可拖拽移動div!

但是!經過測試,這種通過修改position屬性的方法並不可行!在理論上這種方法確實完成了生成分隔開的div和自由拖拽div兩種功能。但在實踐中,由於受到拖拽的div的position會由靜態static轉變為絕對absolute,實現了空間自由拖拽移動,但其他div仍然是靜態位置position:static,他們會自動排列並置頂。也就是說在拖拽div離開其原本位置時,其他div會自動移動、佔據被拖拽的div的位置。在此過程中,div之間的碰撞不可避免,之前分隔div的操作毫無意義,他們依然還是會不受滑鼠控制自行發生碰撞。所以不能通過修改絕對位置position:absolute來消除bug。

為了實現拖拽移動div,position:absolute是必須的。在絕對位置下,想要分隔開div就要使用left和top等屬性來對div位置進行精準定位,並設定全域性變數positionCountX和positionCountY來累計上一個div的位置,和預計生成下一個div的位置。這種靠精準定位div位置生成div的方法成功地解決了自動發生碰撞且強行阻止div移動的bug。

碰撞則是在移動的基礎上進行加工,讓滑鼠事件觸發的div與其他div用迴圈進行兩兩比較,不碰撞則移動,碰撞則彈出警告框。其中也有需要注意的事項,比如要給彈框上鎖(關於上鎖詳情看程式碼,解釋看實驗2總結),避免div重合時不停彈框;還有得在彈出警告框前清除滑鼠移動事件,因為alert彈框會發生中斷,導致後面的滑鼠抬起事件無效,這樣div會在滑鼠沒有按下時也一直跟隨著滑鼠箭頭;此外最好設定在div碰撞後自動移回div碰撞前位置,避免在div碰撞後重疊,這會導致點選事件觸發彈框而不是拖拽div移動碰撞觸發,並因為alert彈框中斷div移動導致兩個div在碰撞後出現無法分開的情況。

此外關於div是否發生碰撞判定的演算法,我是覺得這很有趣的。div怎麼判斷是否與其他div發生碰撞呢?這實際上是兩個問題,一個是div與其他div比較,資料是沒有自己與他人這種概念的;另一個是怎麼判斷碰撞。關於第一個問題,是靠滑鼠拖拽事件區分目標div與所有div,用迴圈將目標div與所有div的比較轉換為兩兩比較,在比較前還要用if語句避免與自身比較,發生自己與自己碰撞的烏龍;關於第二個問題怎麼判斷碰撞,一般直接的演算法是先判斷兩個div是否在同一高度範圍,這需要這兩個目標div的top和bottom進行多次比較,然後才是判斷兩個目標是否重疊,即這兩個div的left和right進行比較。顯然直接比較演算法需要比較很多次,比較同一高度至少需要比較4次,在同一高度比較是否重疊也需要4次,而且這兩種比較是分先後的,也就是所有情況是4*4,共16種情況。這種演算法就顯得直接比較麻煩,就需要重新構思比較策略。與其分析判斷兩個div碰撞的情況,不如分析判斷兩個div不碰撞的情況,這就將所有情況減少了許多,不產生碰撞只有4種情況,其他就算髮生碰撞。

解決了以上問題,我個人認為在邏輯上,我的實驗原始碼已經盡善盡美,但意外還是發生了。在測試中,發生了移動兩個div尚未碰撞,就彈出碰撞警告框的情況。經過反覆測試並在瀏覽器控制檯輸出資料進行檢視,我得出結論這並不是我演算法設計的問題,而是瀏覽器顯示與計算機計算處理速度不一致,計算機計算速度太快,資料發生了變化,但瀏覽器還來不及更新顯示內容,就被alert彈框強行中斷事件。所以要在alert語句外圍設定setTimeout(),讓計算等待1毫秒,等視窗更新變化。加上setTimeout()語句後,成功解決移動兩個div尚未碰撞,就彈出碰撞警告框的問題。

這次實驗讓我深刻地意識到了重複不停思考的意義。就像research(研究)由re-字首(表重複)和search(搜尋)組成,實驗研究就是要不停地探索、不停地測試、不停地思考。