几年前,我参与设计开发一个房产网的项目,我负责前端工作,由于项目经理要求比较高,参考了很多房产类网站比较优秀的功能,想把别人比较优秀的设计和想法集合到一起,那时的设计稿和功能实现,简直就是改了又改,今天做好的一个很好的效果,可能第二天就要推到重来,算了,不说这些了,还是说说我们今天要讲解的案例吧,不知道大家访问过搜房网没有(完全没有做广告之嫌,搜房网,可以给点广告费不),其中有一个功能产品经理特别喜欢,那,就是下面的这个:
这是现在的效果,可能改了一些,原来的效果是,里面的这张图是可以上下左右拖动的,然后房子上面的显示的楼栋号,也跟着图片一起移动,当时js能力还不行,未能实现项目经理的要求,不过后来项目经理又把这个效果推掉了,换了另外的一个效果
尽管项目经理不想要这个效果了,但是当时就在我心里留下了一个节,到今天都忘不了这个梗。
好了,这就是我今天想写这篇博客的初衷,希望能给想实现这类拖拽效果,但是不知道该怎么去实现的同学,提供一种思路,不给青春留遗憾,当然实现拖拽的方法有很多,这里就只介绍JavaScript中的一种方法,慢慢体会一下其中的原理!
好了,梗也说完了,开始正题,我们先要明白,拖拽到底是一个什么东西,你也知道,我也知道,但是我还是想来描述一下:
拖拽就是一个容器,你用鼠标可以在页面上拖着到处跑,废话,精确的描述应该是,鼠标移到容器上,然后鼠标按下去,注意要按着不放,然后拖动鼠标,容器能跟着鼠标跑,松开鼠标,容器就停在那里不动了,现实中的例子就是桌子上有一个盒子,我用手放在盒子上,然后移动盒子,手停盒子停,手拿开,盒子不动了,嘻嘻,都懂了哈!
别以为上面说了一堆的废话,我们可以从中得到很多的信息,总结如下就是:
拖拽 = 鼠标按下 + 鼠标移动 + 鼠标弹上
这样就完成了一个拖拽任务,好了,原来这就是拖拽的原理,想实现拖拽,自然实现上面的3个动作,便可以模拟拖拽效果,好,对应JavaScript中的语法就是需要实现这3个动作:
onmousedown , onmousemove , onmouseup
实现的代码就应该是:
obj.onmousedown = function(ev){ obj.onmousemove = function(ev){ } ; obj.onmouseup = function(ev){ }; }
为什么后面2个动作要写的里面,好好回味一下,好了,第一步的大概思路就有了,下一步就需要考虑怎么让物体跟着鼠标一起移动,思路大概是这样的:
首先物体是需要决定定位的,因为我们需要操作它的left和top值,才能让它移动,然后就是要考虑鼠标了,鼠标位移,本身就会有一个距离,如果我们知道鼠标移动了多远,然后把这个距离给物体,那物体是不是也和鼠标一样,移动了相同的距离,这不就实现拖拽了吗?哈哈,思路一点点有,感觉萌萌哒~ 现在的问题就是怎么获取鼠标的距离,如果需要深入了解,请复习一下盒子模型,这里我就不说了,很多大神也有相关的博客,我用一张图表示一下:
说明:蓝色框为屏幕宽高,黑色粗框为浏览器可视区宽高(浏览器缩小效果),黑色细框为鼠标要拖拽的对象,如图可知,获取鼠标的坐标,可以用event.clientX,event.clientY来获取,哦了;
计算的大致原理可以参照下图:
说明:左边为初始位置,右边为目标位置,原点为鼠标位置,大黑框为浏览器可视宽度,小黑框为拖拽对象,看拖拽对象到目标位置的状态,获取鼠标的最终位置,再减去鼠标距离对象的差值,再赋值给对象的top,left值,也可以获取鼠标的位置差值,再用初始的top,left值加上差值,我们采用第一种,第二种也可以,自己去试一下:
obj.onmousedown = function(ev){ var ev = ev || event; var disX = ev.clientX - this.offsetLeft,disY = ev.clientY - this.offsetTop; document.onmousemove = function(ev){ var ev = ev || event; obj.style.left = ev.clientX - disX + 'px'; obj.style.top = ev.clientY - disY + 'px'; }; document.onmouseup = function(ev){ var ev = ev || event; document.onmousemove = document.onmouseup = null; }; }
这里说明一下:onmousemove和onmouseup之所以用document对象而不用obj对象,是因为如果用obj对象,鼠标在obj内部还好,如果在obj外面的话,拖拽会很怪异,你也可以改成obj体会一下,最后我们在鼠标弹起的时候将事件都清空;
上面的基本拖拽就算完成了,但是细心的同学一定会问,如果页面上有文字的话,拖拽物体会将文字选中,这效果岂不是怪怪的,没错,这是因为拖拽的时候触发了浏览器的默认选择事件,所以,在拖拽的时候,我们要清除这个默认事件,那怎么清除呢?
下面给一个兼容性写法:
if(ev.stopPropagation){ ev.stopPropagation(); }else{ ev.cancelBubble = true; //兼容IE } //简写成 ev.stopPropagation ? ev.stopPropagation() : ev.cancelBubble = true;
将上面的代码放在onmousedown下,鼠标按下就清除浏览器默认事件,文字就不会被选中了,好了,一个简单的拖拽效果就完成了,当然你现在是看不到效果,之所以不给demo链接是为了让你自己试着写一写,这样印象更深刻,
好了,那问题又来了,到这里就这样完了吗?。。。。。。按本人的风格,当然没有,干货还在后面!
如果我想实现这样一个效果,就是这一个大的容器里面(可以是box,也可以是document),怎么样能让我们的拖拽对象不跑出去呢,换句话说,拖到边缘就拖不动了,耶,是不是很多人想要实现的效果,哈哈,我们看看实现的原理是什么:
现实生活中,一个物体在一个盒子里跑不出去,是因为有堵墙,那我们只要能模拟出这堵墙,就可以把物体框起来,那这堵墙要怎么做呢?我们可以换个思路,当拖拽对象拖到边缘的时候,比如说拖到右边,我们将它的left固定住,是不是就不能再往右了,因为left值不能再加了,那么拖到底部,同理我们将top值固定住,就不能再往下拖了,理解吗?
最终的结果就是如下:
//左侧 if(obj.offsetLeft <=0){ obj.style.left = 0; }; //右侧 if(obj.offsetLeft >= pWidth - oWidth){ obj.style.left = pWidth - oWidth + 'px'; }; //上面 if(obj.offsetTop <= 0){ obj.style.top = 0; }; //下面 if(obj.offsetTop >= pHeight - oHeight){ obj.style.top = pHeight - oHeight + 'px'; };
说明:pWidth,pHeight 表示父级元素的宽高(这里是表示相对于父级的宽高限制),oWidth,oHeigt表示拖拽元素的宽高
最后,我将整个拖拽代码整理了一下:
/* 参数说明: 元素绝对定位,父级相对定位,如果父级为window,则可以不用 传一个参数,表示父级为window,物体相对于window范围拖动 传2个参数,则父级为第二个参数,物体相对于父级范围拖动 参数为id值 */ function drag(obj,parentNode){ var obj = document.getElementById(obj); if(arguments.length == 1){ var parentNode = window.self; var pWidth = parentNode.innerWidth,pHeight = parentNode.innerHeight; }else{ var parentNode = document.getElementById(parentNode); var pWidth = parentNode.offsetWidth,pHeight = parentNode.offsetHeight; } obj.onmousedown = function(ev){ var ev = ev || event; var disX = ev.clientX - this.offsetLeft,disY = ev.clientY - this.offsetTop; var oWidth = obj.offsetWidth,oHeight = obj.offsetHeight; //阻止冒泡时间 ev.stopPropagation ? ev.stopPropagation() : ev.cancelBubble = true; document.onmousemove = function(ev){ var ev = ev || event; obj.style.left = ev.clientX - disX + 'px'; obj.style.top = ev.clientY - disY + 'px'; //左侧 if(obj.offsetLeft <=0){ obj.style.left = 0; }; //右侧 if(obj.offsetLeft >= pWidth - oWidth){ obj.style.left = pWidth - oWidth + 'px'; }; //上面 if(obj.offsetTop <= 0){ obj.style.top = 0; }; //下面 if(obj.offsetTop >= pHeight - oHeight){ obj.style.top = pHeight - oHeight + 'px'; }; }; document.onmouseup = function(ev){ var ev = ev || event; document.onmousemove = document.onmouseup = null; }; } }
说明:我这里处理的效果是,如果传一个参数,表示相对的对象是window对象,如果传2个参数,第一个是拖拽对象,第二个为相对父级
开篇就说了,搜房网的那个图片拖拽效果是我的一个心结,我写了一个类似的效果,供大家参考,因为自己没有买服务器,所以效果我就不展示了,直接把代码贴出来,供大家参考:
css:
<style> .box{ width:600px; height:400px; margin:50px auto; position:relative; overflow:hidden; } #box{ width:1000px; height:800px; position:absolute; left:50%; top:50%; margin:-400px 0 0 -500px; } #pic{ width:800px; height:600px; background:url(images/pic1.jpg) no-repeat; position:absolute; left:100px; top:100px; } #pic:hover{ cursor:move; } </style>
html:
<div class="box"> <div id="box"> <div id="pic"></div> </div> </div>
javascript:
window.onload = function(){ drag("pic","box"); function drag(obj,parentNode){ var obj = document.getElementById(obj); if(arguments.length == 1){ var parentNode = window.self; var pWidth = parentNode.innerWidth,pHeight = parentNode.innerHeight; }else{ var parentNode = document.getElementById(parentNode); var pWidth = parentNode.offsetWidth,pHeight = parentNode.offsetHeight; } obj.onmousedown = function(ev){ var ev = ev || event; var disX = ev.clientX - this.offsetLeft,disY = ev.clientY - this.offsetTop; var oWidth = obj.offsetWidth,oHeight = obj.offsetHeight; //阻止冒泡时间 ev.stopPropagation ? ev.stopPropagation() : ev.cancelBubble = true; document.onmousemove = function(ev){ var ev = ev || event; obj.style.left = ev.clientX - disX + 'px'; obj.style.top = ev.clientY - disY + 'px'; //左侧 if(obj.offsetLeft <=0){ obj.style.left = 0; }; //右侧 if(obj.offsetLeft >= pWidth - oWidth){ obj.style.left = pWidth - oWidth + 'px'; }; //上面 if(obj.offsetTop <= 0){ obj.style.top = 0; }; //下面 if(obj.offsetTop >= pHeight - oHeight){ obj.style.top = pHeight - oHeight + 'px'; }; }; document.onmouseup = function(ev){ var ev = ev || event; document.onmousemove = document.onmouseup = null; }; } } }
效果完全是用的那个封装代码块,引用起来也挺方便,有人会问了,你这用的id获取DOM元素,一个页面只能用一次啊,如果页面多次使用呢,有道理,解决方案之一,那就命名不同的id呗,又不犯法,方案二,获取id的地方改成获取class,但是要注意的是,getElementsByClassName是获取的class集合,需要改写一下,这里我就不写了,有兴趣的同学自行改写一下,好了,到这里真的结束了!

去掉重复并排序的方法:1、使用“Array.from(new Set(arr))”或者“[…new Set(arr)]”语句,去掉数组中的重复元素,返回去重后的新数组;2、利用sort()对去重数组进行排序,语法“去重数组.sort()”。

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于Symbol类型、隐藏属性及全局注册表的相关问题,包括了Symbol类型的描述、Symbol不会隐式转字符串等问题,下面一起来看一下,希望对大家有帮助。

怎么制作文字轮播与图片轮播?大家第一想到的是不是利用js,其实利用纯CSS也能实现文字轮播与图片轮播,下面来看看实现方法,希望对大家有所帮助!

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于对象的构造函数和new操作符,构造函数是所有对象的成员方法中,最早被调用的那个,下面一起来看一下吧,希望对大家有帮助。

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于面向对象的相关问题,包括了属性描述符、数据描述符、存取描述符等等内容,下面一起来看一下,希望对大家有帮助。

方法:1、利用“点击元素对象.unbind("click");”方法,该方法可以移除被选元素的事件处理程序;2、利用“点击元素对象.off("click");”方法,该方法可以移除通过on()方法添加的事件处理程序。

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于BOM操作的相关问题,包括了window对象的常见事件、JavaScript执行机制等等相关内容,下面一起来看一下,希望对大家有帮助。

foreach不是es6的方法。foreach是es3中一个遍历数组的方法,可以调用数组的每个元素,并将元素传给回调函数进行处理,语法“array.forEach(function(当前元素,索引,数组){...})”;该方法不处理空数组。


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

禅工作室 13.0.1
功能强大的PHP集成开发环境

记事本++7.3.1
好用且免费的代码编辑器

SecLists
SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。

ZendStudio 13.5.1 Mac
功能强大的PHP集成开发环境

EditPlus 中文破解版
体积小,语法高亮,不支持代码提示功能