javascript實(shí)現(xiàn)PC網(wǎng)頁里的拖拽效果
幾年前,我參與設(shè)計(jì)開發(fā)一個房產(chǎn)網(wǎng)的項(xiàng)目,我負(fù)責(zé)前端工作,由于項(xiàng)目經(jīng)理要求比較高,參考了很多房產(chǎn)類網(wǎng)站比較優(yōu)秀的功能,想把別人比較優(yōu)秀的設(shè)計(jì)和想法集合到一起,那時的設(shè)計(jì)稿和功能實(shí)現(xiàn),簡直就是改了又改,今天做好的一個很好的效果,可能第二天就要推到重來,算了,不說這些了,還是說說我們今天要講解的案例吧,不知道大家訪問過搜房網(wǎng)沒有(完全沒有做廣告之嫌,搜房網(wǎng),可以給點(diǎn)廣告費(fèi)不),其中有一個功能產(chǎn)品經(jīng)理特別喜歡,那,就是下面的這個:
這是現(xiàn)在的效果,可能改了一些,原來的效果是,里面的這張圖是可以上下左右拖動的,然后房子上面的顯示的樓棟號,也跟著圖片一起移動,當(dāng)時js能力還不行,未能實(shí)現(xiàn)項(xiàng)目經(jīng)理的要求,不過后來項(xiàng)目經(jīng)理又把這個效果推掉了,換了另外的一個效果
盡管項(xiàng)目經(jīng)理不想要這個效果了,但是當(dāng)時就在我心里留下了一個節(jié),到今天都忘不了這個梗。
好了,這就是我今天想寫這篇博客的初衷,希望能給想實(shí)現(xiàn)這類拖拽效果,但是不知道該怎么去實(shí)現(xiàn)的同學(xué),提供一種思路,不給青春留遺憾,當(dāng)然實(shí)現(xiàn)拖拽的方法有很多,這里就只介紹JavaScript中的一種方法,慢慢體會一下其中的原理!
好了,梗也說完了,開始正題,我們先要明白,拖拽到底是一個什么東西,你也知道,我也知道,但是我還是想來描述一下:
拖拽就是一個容器,你用鼠標(biāo)可以在頁面上拖著到處跑,廢話,精確的描述應(yīng)該是,鼠標(biāo)移到容器上,然后鼠標(biāo)按下去,注意要按著不放,然后拖動鼠標(biāo),容器能跟著鼠標(biāo)跑,松開鼠標(biāo),容器就停在那里不動了,現(xiàn)實(shí)中的例子就是桌子上有一個盒子,我用手放在盒子上,然后移動盒子,手停盒子停,手拿開,盒子不動了,嘻嘻,都懂了哈!
別以為上面說了一堆的廢話,我們可以從中得到很多的信息,總結(jié)如下就是:
拖拽 = 鼠標(biāo)按下 + 鼠標(biāo)移動 + 鼠標(biāo)彈上
這樣就完成了一個拖拽任務(wù),好了,原來這就是拖拽的原理,想實(shí)現(xiàn)拖拽,自然實(shí)現(xiàn)上面的3個動作,便可以模擬拖拽效果,好,對應(yīng)JavaScript中的語法就是需要實(shí)現(xiàn)這3個動作:
onmousedown , onmousemove , onmouseup
實(shí)現(xiàn)的代碼就應(yīng)該是:
obj.onmousedown = function(ev){ obj.onmousemove = function(ev){ } ; obj.onmouseup = function(ev){ }; }
為什么后面2個動作要寫的里面,好好回味一下,好了,第一步的大概思路就有了,下一步就需要考慮怎么讓物體跟著鼠標(biāo)一起移動,思路大概是這樣的:
首先物體是需要決定定位的,因?yàn)槲覀冃枰僮魉膌eft和top值,才能讓它移動,然后就是要考慮鼠標(biāo)了,鼠標(biāo)位移,本身就會有一個距離,如果我們知道鼠標(biāo)移動了多遠(yuǎn),然后把這個距離給物體,那物體是不是也和鼠標(biāo)一樣,移動了相同的距離,這不就實(shí)現(xiàn)拖拽了嗎?哈哈,思路一點(diǎn)點(diǎn)有,感覺萌萌噠~ 現(xiàn)在的問題就是怎么獲取鼠標(biāo)的距離,如果需要深入了解,請復(fù)習(xí)一下盒子模型,這里我就不說了,很多大神也有相關(guān)的博客,我用一張圖表示一下:
說明:藍(lán)色框?yàn)槠聊粚捀撸谏挚驗(yàn)闉g覽器可視區(qū)寬高(瀏覽器縮小效果),黑色細(xì)框?yàn)槭髽?biāo)要拖拽的對象,如圖可知,獲取鼠標(biāo)的坐標(biāo),可以用event.clientX,event.clientY來獲取,哦了;
計(jì)算的大致原理可以參照下圖:
說明:左邊為初始位置,右邊為目標(biāo)位置,原點(diǎn)為鼠標(biāo)位置,大黑框?yàn)闉g覽器可視寬度,小黑框?yàn)橥献ο?,看拖拽對象到目?biāo)位置的狀態(tài),獲取鼠標(biāo)的最終位置,再減去鼠標(biāo)距離對象的差值,再賦值給對象的top,left值,也可以獲取鼠標(biāo)的位置差值,再用初始的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對象,是因?yàn)槿绻胦bj對象,鼠標(biāo)在obj內(nèi)部還好,如果在obj外面的話,拖拽會很怪異,你也可以改成obj體會一下,最后我們在鼠標(biāo)彈起的時候?qū)⑹录记蹇眨?/p>
上面的基本拖拽就算完成了,但是細(xì)心的同學(xué)一定會問,如果頁面上有文字的話,拖拽物體會將文字選中,這效果豈不是怪怪的,沒錯,這是因?yàn)橥献У臅r候觸發(fā)了瀏覽器的默認(rèn)選擇事件,所以,在拖拽的時候,我們要清除這個默認(rèn)事件,那怎么清除呢?
下面給一個兼容性寫法:
if(ev.stopPropagation){ ev.stopPropagation(); }else{ ev.cancelBubble = true; //兼容IE } //簡寫成 ev.stopPropagation ? ev.stopPropagation() : ev.cancelBubble = true;
將上面的代碼放在onmousedown下,鼠標(biāo)按下就清除瀏覽器默認(rèn)事件,文字就不會被選中了,好了,一個簡單的拖拽效果就完成了,當(dāng)然你現(xiàn)在是看不到效果,之所以不給demo鏈接是為了讓你自己試著寫一寫,這樣印象更深刻,
好了,那問題又來了,到這里就這樣完了嗎?。。。。。。按本人的風(fēng)格,當(dāng)然沒有,干貨還在后面!
如果我想實(shí)現(xiàn)這樣一個效果,就是這一個大的容器里面(可以是box,也可以是document),怎么樣能讓我們的拖拽對象不跑出去呢,換句話說,拖到邊緣就拖不動了,耶,是不是很多人想要實(shí)現(xiàn)的效果,哈哈,我們看看實(shí)現(xiàn)的原理是什么:
現(xiàn)實(shí)生活中,一個物體在一個盒子里跑不出去,是因?yàn)橛卸聣?,那我們只要能模擬出這堵墻,就可以把物體框起來,那這堵墻要怎么做呢?我們可以換個思路,當(dāng)拖拽對象拖到邊緣的時候,比如說拖到右邊,我們將它的left固定住,是不是就不能再往右了,因?yàn)閘eft值不能再加了,那么拖到底部,同理我們將top值固定住,就不能再往下拖了,理解嗎?
最終的結(jié)果就是如下:
//左側(cè) if(obj.offsetLeft <=0){ obj.style.left = 0; }; //右側(cè) 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表示拖拽元素的寬高
最后,我將整個拖拽代碼整理了一下:
/* 參數(shù)說明: 元素絕對定位,父級相對定位,如果父級為window,則可以不用 傳一個參數(shù),表示父級為window,物體相對于window范圍拖動 傳2個參數(shù),則父級為第二個參數(shù),物體相對于父級范圍拖動 參數(shù)為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'; //左側(cè) if(obj.offsetLeft <=0){ obj.style.left = 0; }; //右側(cè) 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; }; } }
說明:我這里處理的效果是,如果傳一個參數(shù),表示相對的對象是window對象,如果傳2個參數(shù),第一個是拖拽對象,第二個為相對父級
開篇就說了,搜房網(wǎng)的那個圖片拖拽效果是我的一個心結(jié),我寫了一個類似的效果,供大家參考,因?yàn)樽约簺]有買服務(wù)器,所以效果我就不展示了,直接把代碼貼出來,供大家參考:
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'; //左側(cè) if(obj.offsetLeft <=0){ obj.style.left = 0; }; //右側(cè) 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集合,需要改寫一下,這里我就不寫了,有興趣的同學(xué)自行改寫一下,好了,到這里真的結(jié)束了!
相關(guān)文章
學(xué)習(xí)使用bootstrap基本控件(table、form、button)
這篇文章主要教會大家學(xué)習(xí)使用bootstrap基本控件,如table、form、button控件,感興趣的小伙伴們可以參考一下2016-04-04javascript省市級聯(lián)功能實(shí)現(xiàn)方法實(shí)例詳解
這篇文章主要介紹了javascript省市級聯(lián)功能實(shí)現(xiàn)方法,以不同實(shí)例形式分析了JavaScript實(shí)現(xiàn)省市級聯(lián)菜單的具體技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-10-10值得分享和收藏的xmlplus組件學(xué)習(xí)教程
值得分享和收藏的xmlplus組件學(xué)習(xí)教程,xmlplus是一個設(shè)計(jì)非常獨(dú)特 JavaScript 框架,用于快速開發(fā)前后端項(xiàng)目,感興趣的小伙伴們可以參考一下2017-05-05js canvas實(shí)現(xiàn)擦除效果示例代碼
擦除效果在我們?nèi)粘i_發(fā)中也是時有見到的,通過擦除效果大大加強(qiáng)了與用戶的交互性,所以下面這篇文章主要給大家介紹了利用js和canvas實(shí)現(xiàn)擦除效果的相關(guān)資料,文中給出了詳細(xì)的介紹和示例代碼,需要的朋友可以參考借鑒,下面來一起看看吧。2017-04-04javascript搜索框效果實(shí)現(xiàn)方法
這篇文章主要介紹了javascript搜索框效果實(shí)現(xiàn)方法,可實(shí)現(xiàn)顯示默認(rèn)提示文字的搜索框效果,非常簡單實(shí)用,需要的朋友可以參考下2015-05-05微信小程序開發(fā)的四十個技術(shù)竅門總結(jié)(推薦)
這篇文章主要給大家介紹了微信小程序開發(fā)的四十個技術(shù)竅門的相關(guān)資料,相信對大家的學(xué)習(xí)或者使用微信小程序具有一定的參考借鑒價值,所以特別推薦給大家,需要的朋友們可以一起來看看吧。2017-01-01input輸入框限制只能輸入數(shù)字的方法實(shí)例(個人認(rèn)為最好的)
在很多業(yè)務(wù)中需要對輸入框進(jìn)行字符限制,比如金額輸入框、手機(jī)號碼輸入框等,下面這篇文章主要給大家介紹了關(guān)于input輸入框限制只能輸入數(shù)字的相關(guān)資料,文中介紹的方法個人認(rèn)為最好的,需要的朋友可以參考下2022-10-10對setInterval在火狐和chrome切換標(biāo)簽產(chǎn)生奇怪的效果之探索,與解決方案!
其實(shí)這個問題,已經(jīng)困擾我很近。就是切換瀏覽器標(biāo)簽之后,再等幾十秒的時間切換回來setInterval就亂了陣,過一會又正常了!IE瀏覽器就沒有這種奇怪的現(xiàn)象!2011-10-10