幾年前,我參與設計開發一個房產網的項目,我負責前端工作,由於專案經理要求比較高,參考了很多房產類網站比較優秀的功能,想把別人比較優秀的設計和想法集合到一起,那時的設計稿和功能實現,簡直就是改了又改,今天做好的一個很好的效果,可能第二天就要推到重來,算了,不說這些了,還是說說我們今天要講解的案例吧,不知道大家訪問過搜房網沒有(完全沒有做廣告之嫌,搜房網,可以給點廣告費不),其中有一個功能產品經理特別喜歡,那,就是下面的這個:
這是現在的效果,可能改了一些,原來的效果是,裡面的這張圖是可以上下左右拖動的,然後房子上面的顯示的樓棟號,也跟著圖片一起移動,當時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操作符,构造函数是所有对象的成员方法中,最早被调用的那个,下面一起来看一下吧,希望对大家有帮助。

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

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

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

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


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

Atom編輯器mac版下載
最受歡迎的的開源編輯器

Dreamweaver Mac版
視覺化網頁開發工具

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器

SAP NetWeaver Server Adapter for Eclipse
將Eclipse與SAP NetWeaver應用伺服器整合。

EditPlus 中文破解版
體積小,語法高亮,不支援程式碼提示功能