JS實現拖拽功能


本文代碼地址(第一節):https://github.com/dirstart/js-exam/blob/master/拖拽div1.html
第二節:https://github.com/dirstart/js-exam/blob/master/拖拽div2.html
第三節:https://github.com/dirstart/js-exam/blob/master/拖拽div3.html

本來只是想要實現鼠標放置並且一個懸浮窗的功能,結果慢慢回顧基礎突然想起以前看到過的拖拽,就都需要定位來說,懸浮窗和拖拽有相似的地方。

那么,怎么實現拖拽呢,我們需要什么呢?

  • 首先我們看功能,拖拽就是 鼠標按住 => 鼠標移動 => 鼠標松開
  • 接着我們需要什么? 最后的結果是拖住的塊要動,而且是跟着鼠標移動 = > 改變塊的 style.top ,要注意, offsetHeight是只讀的,style.top是可寫的,這個是我以前踩過的坑,不過已經忘了是在哪里踩的了。因為要改變,所以我們首先要獲取,因此 => 獲取style的函數

http://www.cnblogs.com/jshen/archive/2012/11/29/2794292.html (關於style.top和offsetTop)

  • 最后我們所記錄下的數據: 1.物塊的起始位置 2.鼠標移動的位置 3.物塊最后的位置
    我們能夠想到的過程: a.鼠標按下,物塊不動(但是告訴瀏覽器我們准備好了,我點着的是box) b.鼠標移動,物塊動,用數組保存鼠標移動的位置 c.鼠標放下,物塊不動,一波操作結束
    實際上塊的移動距離是鼠標mousedown點到mousemove的變動距離

大致就先這樣,現在開始我們的表演。


First Blood : 獲取css屬性

首先證明一下,為什么不直接用DOM的參數來直接獲取屬性?
css部分

		*{margin: 0;padding: 0;}
		#box{
			margin-top: 240px;
			margin-left: 500px;
			width: 100px;
			height: 100px;
			background: #bfbfbf;
			cursor: move;
		}

body部分

	<div id="box"></div>

js部分

	let box=document.getElementById("box");
	// box.style.marginTop=600+"px";
	box.onmouseover=function(event){
		console.log(parseInt(box.style.marginTop));
	}

如上,最后顯示的是NaN,可是我們明明在CSS中寫了margin-top呀,於是我們將注釋去掉,發現去掉之后顯示的是600.
=> 原因在於,style.marginTop獲取不了外部的樣式。
鏈接如下 : http://www.cnblogs.com/cythia/p/6721145.html

所以引出能夠獲取外部樣式的 方法 currentStyle和getComputedStyle.之后,因為IE支持前者,FF和Chrome支持后者。
let oStyle=this.currentStyle?this.currentStyle:window.getComputedStyle(this,null);

	box.onmouseover=function(event){
		let oStyle=this.currentStyle?this.currentStyle:window.getComputedStyle(this,null);
		console.log(parseInt(box.style.marginTop));  // NaN
		console.log(oStyle.height);					// 100px
	}

發現可用,之后進入我們的第二步。


Second , 獲取鼠標的動態並根據此改變我們的塊已達到拖拽的目的

let event=event||window.event; 通過此句獲得當前鼠標的地址,這樣的賦值依舊是為了處理兼容性問題。
!!!!!!!!!!!!很幸運,我們見到了一個新坑,這里報錯了
報錯信息:Uncaught SyntaxError: Identifier 'event' has already been declared
這個錯誤應該是跟let有關,在這里用以前的var就不會出錯。
在這里我先扔出大概的原理鏈接,因為我也不是很懂==,let x=x這樣是會報錯的:

https://www.zhihu.com/question/62966713/answer/204279809

那么我們先用回 var event=event||window.event,繼續我們的旅程。

	let box=document.getElementById("box");
	let disX=disY=0;
	let startX=startY=0;
	let flag=false;
	box.onmousedown=function(event){
		var event=event||window.event;
		// 阻止事件冒泡
		if(event && event.stopPropagation){
			event.stopPropagation();
		}else{
			window.event.cancelBubble=true;
		}
		// 記錄下mousedown點的距離
		flag=true;
		startX=event.clientX;
		startY=event.clientY;
		console.log("down");
	}
	box.onmousemove=function(event){
		if(flag===false) return ;
		let oStyle=this.currentStyle?this.currentStyle:window.getComputedStyle(this,null);
		disX=event.clientX-startX;
		disY=event.clientY-startY;
		box.style.marginLeft=parseInt(oStyle.marginLeft)+disX+"px";
		box.style.marginTop=parseInt(oStyle.marginTop)+disY+"px";
	}	
	document.onmouseup=function(){
		flag=false;
	}

我們發現能夠移動,但是非常卡頓,很不平滑,完全不像每次計算 鼠標變動距離的樣子。

問題已發現:少了一句話。 我們需要在 onmousemove 再加一句話,因為我們的startX始終沒有改變,而實際上每一次的移動都應該改變我們上一次的起始點。

	box.onmousemove=function(event){
                       ............................................
		startX=event.clientX;
		startY=event.clientY;
	}	

加上我們的基本功能就算完成了。

但是還有個問題,當我們的鼠標移出了box的范圍后,因為我們使用的是 box.onmousemove ,我們鼠標到了document就不行了。

這又是我們的思維漏洞,不過改倒是非常容易
box.onmousemove改成'document.onmousemove,將里面的三個 this改成box` 即可。

第一版代碼地址:https://github.com/dirstart/js-exam/blob/master/拖拽div1.html

那么這樣我們的第二階段就OK了,我們先看看網上其他的例子。


現在能想到的優化:1.樣式可以更好看 2.可以給提示和信息 3.不讓div移出瀏覽器框外。


看完fgm.cc里面的拖拽之后的

首先記錄下過程中可能要看的知識點:http://blog.csdn.net/u012309349/article/details/50663841

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<style type="text/css">
		*{margin: 0;padding: 0;}
		ul,li{
			list-style: none;
		}
		#box{
			cursor: move;
			position: absolute;
			top: 50%;
			left : 50%;
			width: 300px;
			height: 150px;
			margin: -75px 0 0 -150px;
			background: #C0E4DF;
			border: 1px solid #3E2D2D;
		}
		html{
			font-size: 12px;
			background: #F0ADAD;
		}
		.header{
			border-bottom: 1px solid #3E2D2D;
			height: 40px;
			line-height: 40px;
			font-size: 16px;
			text-align: center;
			color: #8C1F1F;
		}
	</style>
</head>
<body>
	<div id="box">
		<p class="header">我是被拖拽的小窗口</p>
		<p>拖拽是否開啟</p><span></span>
		<ul>
			<li>位置X : <span id="displayX"></span></li>
			<li>位置Y : <span id="displayY"></span></li>
		</ul>
	</div>
</body>
<script type="text/javascript">
	let oBox=document.getElementById("box"),
		oX=document.getElementById('displayX'),
		oY=document.getElementById('displayY'),
		isDrag=false,
		temX=0,
		temY=0,
		disX=0,
		disY=0;
	oBox.onmousedown=(event)=>{
		var event=event||window.event;
		isDrag=true;
		temX=event.clientX;
		temY=event.clientY;
		disX=temX-oBox.offsetLeft;
		disY=temY-oBox.offsetTop;

		this.setCapture && this.setCapture();

		return false;
	}
	document.onmousemove=(event)=>{
		if(!isDrag) return ;
		var event=event||window.event;
		temX=event.clientX-disX;
		temY=event.clientY-disY;
		oBox.style.left=temX+"px";
		oBox.style.top=temY+"px";
		// oBox.style.marginTop = oBox.style.marginLeft = 0;

	}
	document.onmouseup=(event)=>{
		isDrag=false;
	}
</script>
</html>

這是根據fgm.cc網站修改的部分,我們重點關注一下注釋中的// oBox.style.marginTop = oBox.style.marginLeft = 0;,這里有個問題,如果不加這句話的話鼠標不會保持在我們第一次放下的oBox內的位置。

其實原因是這個樣子的,因為我們初始的時候為了讓元素居中,所以我們用了 top:50%;left:50%; margin-left: #(負一半的元素寬度) ; margin-top: #(負一半的元素高度)
上面的目的是為了居中,而此后,因為我們直接通過 offsetLeft和offsetTop來設定了位置,這個時候的margin-left和margin-top如果不清楚,就會造成 ---- box盒子往 左上偏 。
可以這樣理解 。我們這個時候已經不用 top 和 left 的絕對定位了, 而我們 派出去生物天敵 margin卻還在起着它的作用 ,這就像當年的羅斯福與破壞生物的故事了。

So,我們得加上那句注釋掉的話。

接下來的任務是怎么讓盒子不移出我們的視線之外。

其實照網上的這種寫法,要直接限定盒子簡直是太容易了,不得不承認別人的邏輯比我的好上太多了。代碼如下:

	document.onmousemove=(event)=>{
		if(!isDrag) return ;
		var event=event||window.event;
		temX=event.clientX-disX;
		temY=event.clientY-disY;
		oBox.style.marginTop = oBox.style.marginLeft = 0;

		
		console.log(temX);
		// 做判斷
		temX=temX<0?0:temX;
		temX=temX>maxX?maxX:temX;
		temY=temY<0?0:temY;
		temY=temY>maxY?maxY:temY;

		oBox.style.left=temX+"px";
		oBox.style.top=temY+"px";
		oBox.style.marginTop = oBox.style.marginLeft = 0;

	}

至於如何來美化它,這里就不再闡述了,不過之后再來介紹一種用其他思路來實現的代碼。


Third 第三種方法的實現


<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<style type="text/css">
		*{margin: 0;padding: 0;}
		body{
			background: #6A3545;
		}
		#box{
			position: absolute;
			left: 500px;
			top: 300px;
			width: 100px;
			height: 100px;
			border: 1px solid #fde331;
			background: #1A5F5C;
			cursor: move;
		}
		#temp{
			position: absolute;
			width: 100px;
			height: 100px;
			background: #bfb171;
			opacity: 0.6;
		}
	</style>
</head>
<body>
	<div id="box">
	</div>
</body>
<script type="text/javascript">
	var zIndex=1;
	var oBox=document.getElementById('box');
	function getStyle(ele,attr){
		return ele.currentStyle?ele.currentStyle[attr]:getComputedStyle(ele,null)[attr];
	}
	oBox.onmousedown=function(event){
		var event=event||window.event;
		var disX=event.clientX-oBox.offsetLeft;
		var disY=event.clientY-oBox.offsetTop;
		var maxLeft=document.documentElement.clientWidth - oBox.offsetWidth;
		var maxTop=document.documentElement.clientHeight - oBox.offsetHeight;
		var oTemp=document.createElement("div");
		oTemp["id"]="temp";
		oTemp.style.left=getStyle(oBox,"left");
		oTemp.style.top=getStyle(oBox,"top");
		oTemp.style.zIndex=zIndex++;
		document.body.appendChild(oTemp);
		document.onmousemove=function(event){
			var event=event||window.event;
			var temX=event.clientX-disX;
			var temY=event.clientY-disY;		
			temX=temX>maxLeft?maxLeft:temX;
			temX=temX<0?0:temX;
			temY=temY>maxTop?maxTop:temY;
			temY=temY<0?0:temY;
			oTemp.style.left=temX+"px";
			oTemp.style.top=temY+"px";
			return false;
		}
		document.onmouseup=function(){
			document.onmousemove=null;
			document.onmouseup=null;
			oBox.style.left=oTemp.style.left;
			oBox.style.top=oTemp.style.top;
			document.body.removeChild(oTemp);
		}	
		return false;
	}
</script>
</html>

考慮到用戶交互的移動,這往往是我們真正會做到的東西.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM