zoukankan      html  css  js  c++  java
  • 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;
    			 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%;
    			 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;
    			 100px;
    			height: 100px;
    			border: 1px solid #fde331;
    			background: #1A5F5C;
    			cursor: move;
    		}
    		#temp{
    			position: absolute;
    			 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>
    

    考虑到用户交互的移动,这往往是我们真正会做到的东西.

  • 相关阅读:
    spymemcache与spring结合
    Turtle库的学习积累
    汉诺塔绘图学习
    计算圆周率π和显示进度条
    川菜 in English
    一些必不可少的Sublime Text 2插件 钟磊的专栏 博客频道 CSDN.NET
    \usepackage{natbib}在latex模板写作中的心得_格致轩_百度空间
    Geant4新版本:新安装方法,新运行方式
    ubuntu系统备份
    Latex数学公式中的空格
  • 原文地址:https://www.cnblogs.com/can-i-do/p/7345644.html
Copyright © 2011-2022 走看看