[1]原理介紹 [2]代碼實現 [3]代碼優化 [4]拖拽衝突 [5]IE相容 ...
×
目錄
[1]原理介紹 [2]代碼實現 [3]代碼優化[4]拖拽衝突[5]IE相容前面的話
從本文開始,介紹javascript動畫系列。javascript本身是具有原生拖放功能的,但是由於相容性問題,以及功能實現的方式,用的不是很廣泛。javascript動畫廣泛使用的還是模擬拖拽。本文將詳細介紹該內容
原理介紹
模擬拖拽最終效果和在桌面上移動文件夾的效果類似
滑鼠按下時,拖拽開始。滑鼠移動時,被拖拽元素跟著滑鼠一起移動。滑鼠抬起時,拖拽結束
所以,拖拽的重點是確定被拖拽元素是如何移動的
假設,滑鼠按下時,滑鼠對象的clientX和clientY分別為x1和x2。元素距離視口左上角x軸和y軸分別為x0和y0
滑鼠移動的某一時刻,clientX和clientY分別為x2和y2
所以,元素移動的x軸和y軸距離分別為x2-x1和y2-y1
元素移動後,元素距離視口左上角x軸和y軸的位置分別為
X = x0 + (x2-x1)
Y = y0 + (y2-y1)
代碼實現
將上面的原理用代碼實現如下
滑鼠按下時,初始態的x0和y0分別用offsetLeft和offsetTop表示
滑鼠移動時,瞬時態的x和y分別賦值為定位後元素的left和top
<div id="test" style="height: 100px;width: 100px;background:pink;position:absolute;top:0;left:0;"></div> <script> test.onmousedown = function(e){ e = e || event; //獲取元素距離定位父級的x軸及y軸距離 var x0 = this.offsetLeft; var y0 = this.offsetTop; //獲取此時滑鼠距離視口左上角的x軸及y軸距離 var x1 = e.clientX; var y1 = e.clientY; test.onmousemove = function(e){ e = e || event; //獲取此時滑鼠距離視口左上角的x軸及y軸距離 x2 = e.clientX; y2 = e.clientY; //計算此時元素應該距離視口左上角的x軸及y軸距離 var X = x0 + (x2 - x1); var Y = y0 + (y2 - y1); //將X和Y的值賦給left和top,使元素移動到相應位置 test.style.left = X + 'px'; test.style.top = Y + 'px'; } test.onmouseup = function(e){ //當滑鼠抬起時,拖拽結束,則將onmousemove賦值為null即可 test.onmousemove = null; } } </script>
代碼優化
使用上面的代碼時,會出現一個問題。當滑鼠拖動的太快,比onmousemove事件的觸發間隔還要快時,滑鼠就會從元素上離開。這樣就停止了元素的拖拽過程
此時,如果把mousemove和mouseup事件都加在document上時,即可解決
<div id="test" style="height: 100px;width: 100px;background:pink;position:absolute;top:0;left:0;"></div> <script> test.onmousedown = function(e){ e = e || event; //獲取元素距離定位父級的x軸及y軸距離 var x0 = this.offsetLeft; var y0 = this.offsetTop; //獲取此時滑鼠距離視口左上角的x軸及y軸距離 var x1 = e.clientX; var y1 = e.clientY; document.onmousemove = function(e){ e = e || event; //獲取此時滑鼠距離視口左上角的x軸及y軸距離 x2 = e.clientX; y2 = e.clientY; //計算此時元素應該距離視口左上角的x軸及y軸距離 var X = x0 + (x2 - x1); var Y = y0 + (y2 - y1); //將X和Y的值賦給left和top,使元素移動到相應位置 test.style.left = X + 'px'; test.style.top = Y + 'px'; } document.onmouseup = function(e){ //當滑鼠抬起時,拖拽結束,則將onmousemove賦值為null即可 document.onmousemove = null; } } </script>
拖拽衝突
由於文字和圖片預設支持原生拖放,如果將原生拖放和模擬拖拽摻雜在一起,將造成與預想效果不符的情況
如果拖放的元素內容存在文字,且文字被選中會觸發文字的原生拖放效果
在文字上面雙擊滑鼠,即可選中文字,再移動滑鼠時,會觸發文字的原生拖放效果,如下所示
只要在onmousedown事件阻止瀏覽器的預設行為即可
<div id="test" style="height: 100px;width: 100px;background:pink;position:absolute;top:0;left:0;">測試文字</div> <script> test.onmousedown = function(e){ e = e || event; //獲取元素距離定位父級的x軸及y軸距離 var x0 = this.offsetLeft; var y0 = this.offsetTop; //獲取此時滑鼠距離視口左上角的x軸及y軸距離 var x1 = e.clientX; var y1 = e.clientY; document.onmousemove = function(e){ e = e || event; //獲取此時滑鼠距離視口左上角的x軸及y軸距離 x2 = e.clientX; y2 = e.clientY; //計算此時元素應該距離視口左上角的x軸及y軸距離 var X = x0 + (x2 - x1); var Y = y0 + (y2 - y1); //將X和Y的值賦給left和top,使元素移動到相應位置 test.style.left = X + 'px'; test.style.top = Y + 'px'; } document.onmouseup = function(e){ //當滑鼠抬起時,拖拽結束,則將onmousemove賦值為null即可 document.onmousemove = null; } //阻止預設行為 return false; } </script>
IE相容
以上代碼在IE8-瀏覽器中仍然無法阻止預設行為。此時,為了實現IE相容,需要使用全局捕獲setCapture()和釋放捕獲releaseCapture()
首先,先看一下全局捕獲的效果
下麵代碼中,開啟全局捕獲之後,頁面中的所有點擊效果,都相當於針對按鈕一的點擊效果。釋放捕獲後,效果消失
[註意]IE瀏覽器完全支持全局捕獲;chrome不支持,使用全局捕獲會報錯;firefox不報錯,但靜默失敗
<button id="btn1">按鈕一</button> <button id="btn2">開啟按鈕一的全局捕獲</button> <script> btn1.onclick = function(){ alert(1); } btn2.onclick = function(){ if(btn1.setCapture){ if(btn2.innerHTML.charAt(0) == '開'){ btn1.setCapture(); btn2.innerHTML = '關閉按鈕一的全局捕獲'; }else{ btn1.releaseCapture(); btn2.innerHTML = '開啟按鈕一的全局捕獲'; } } } </script>
通過在IE瀏覽器設置全局捕獲來達到取消文字原生拖放的預設行為
<div id="test" style="height: 100px;width: 100px;background:pink;position:absolute;top:0;left:0;">測試文字</div> <script> test.onmousedown = function(e){ e = e || event; //獲取元素距離定位父級的x軸及y軸距離 var x0 = this.offsetLeft; var y0 = this.offsetTop; //獲取此時滑鼠距離視口左上角的x軸及y軸距離 var x1 = e.clientX; var y1 = e.clientY; document.onmousemove = function(e){ e = e || event; //獲取此時滑鼠距離視口左上角的x軸及y軸距離 x2 = e.clientX; y2 = e.clientY; //計算此時元素應該距離視口左上角的x軸及y軸距離 var X = x0 + (x2 - x1); var Y = y0 + (y2 - y1); //將X和Y的值賦給left和top,使元素移動到相應位置 test.style.left = X + 'px'; test.style.top = Y + 'px'; } document.onmouseup = function(e){ //當滑鼠抬起時,拖拽結束,則將onmousemove賦值為null即可 document.onmousemove = null; //釋放全局捕獲 if(test.releaseCapture){ test.releaseCapture(); } } //阻止預設行為 return false; //IE8-瀏覽器阻止預設行為 if(test.setCapture){ test.setCapture(); } } </script>