一区二区久久-一区二区三区www-一区二区三区久久-一区二区三区久久精品-麻豆国产一区二区在线观看-麻豆国产视频

JavaScript 拖放效果代碼

這個程序的原型是在做圖片切割效果的時候做出來的,那時參考了好幾個同類的效果,跟muxrwc和BlueDestiny學習了不少東西。
雖然每次整理都覺得很好了,不過每隔一段時間又會發現得某個地方可以改善,某個地方有錯誤,某些需求需要實現,就像自己學習的知識那樣。
這里考慮到有的人可能只需要簡單的拖放,所以有一個簡化版的拖放SimpleDrag,方便學習。
效果預覽
ps:在maxthon下如果開啟廣告過濾的話很可能會被過濾掉(不知有什么方法可以避免)。
程序說明
【程序原理】
這里以SimpleDrag為例說一下基本原理。
首先初始化程序中要一個拖放對象:
復制代碼 代碼如下:
this.Drag = $(drag);

還要兩個參數在開始時記錄鼠標相對拖放對象的坐標:
復制代碼 代碼如下:
this._x = this._y = 0;

還有兩個事件對象函數用于添加移除事件:
復制代碼 代碼如下:
this._fM = BindAsEventListener(this, this.Move);
this._fS = Bind(this, this.Stop);

分別是拖動程序和停止拖動程序。
拖放對象的position必須是absolute絕對定位:
復制代碼 代碼如下:
this.Drag.style.position = "absolute";

最后把Start開始拖放程序綁定到拖放對象mousedown事件:
addEventHandler(this.Drag, "mousedown", BindAsEventListener(this, this.Start));
鼠標在拖放對象按住,就會觸發Start程序,主要是用來準備拖動,在這里記錄鼠標相對拖放對象的坐標:
復制代碼 代碼如下:
this._x = oEvent.clientX - this.Drag.offsetLeft;
this._y = oEvent.clientY - this.Drag.offsetTop;

并把_fM拖動程序和_fS停止拖動程序分別綁定到document的mousemove和mouseup事件:
復制代碼 代碼如下:
addEventHandler(document, "mousemove", this._fM);
addEventHandler(document, "mouseup", this._fS);

注意要綁定到document才可以保證事件在整個窗口文檔中都有效,如果只綁定到拖放對象就很容易出現拖太快就脫節的現象。
當鼠標在文檔上移動時,就會觸發Move程序了,這里就是實現拖動的程序。
通過現在鼠標的坐標值跟開始拖動時鼠標相對的坐標值的差就可以得到拖放對象應該設置的left和top了:
this.Drag.style.left = oEvent.clientX - this._x + "px";
this.Drag.style.top = oEvent.clientY - this._y + "px";
最后放開鼠標后就觸發Stop程序結束拖放。
這里的主要作用是把Start程序中給document添加的事件移除:
removeEventHandler(document, "mousemove", this._fM);
removeEventHandler(document, "mouseup", this._fS);
這樣一個簡單的拖放程序就做好了,下面說說其他擴展和細節部分。
【拖放鎖定】
鎖定分三種,分別是:水平方向鎖定(LockX)、垂直方向鎖定(LockY)、完全鎖定(Lock)。
這個比較簡單,水平和垂直方向的鎖定只要在Move判斷是否鎖定再設置left和top就行,如果是完全鎖定就直接返回。
if(!this.LockX){ this.Drag.style.left = ; }
if(!this.LockY){ this.Drag.style.top = ; }
【觸發對象】
觸發對象是用來觸發拖放程序的,程序中通過Handle屬性設置。有的時候不需要整個拖放對象都用來觸發,這時就需要觸發對象了。
使用了觸發對象后,進行移動的還是拖放對象,只是用觸發對象來觸發拖放(一般的使用是把觸發對象放到拖放對象里面)。
ps:觸發對象的另一個用法是通過設置相同的Handle,實現一個觸發對象同時拖放多個拖放對象。
【范圍限制】
要設置范圍限制必須先把Limit設為true。范圍限制分兩種,分別是固定范圍和容器范圍限制,主要在Move程序中設置。
原理是當比較的值超過范圍時,修正left和top要設置的值使拖放對象能保持在設置的范圍內。
【固定范圍限制】
容器范圍限制就是指定上下左右的拖放范圍。
各個屬性的意思是:
上(mxTop):top限制;
下(mxBottom):top+offsetHeight限制;
左(mxLeft):left限制;
右(mxRight):left+offsetWidth限制。
如果范圍設置不正確,可能導致上下或左右同時超過范圍的情況,程序中有一個Repair程序用來修正范圍參數的。
Repair程序會在程序初始化和Start程序中執行,在Repair程序中修正mxRight和mxBottom:
this.mxRight = Math.max(this.mxRight, this.mxLeft + this.Drag.offsetWidth);
this.mxBottom = Math.max(this.mxBottom, this.mxTop + this.Drag.offsetHeight);
其中mxLeft+offsetWidth和mxTop+offsetHeight分別是mxRight和mxBottom的最小范圍值。
根據范圍參數修正移動參數:
iLeft = Math.max(Math.min(iLeft, mxRight - this.Drag.offsetWidth), mxLeft);
iTop = Math.max(Math.min(iTop, mxBottom - this.Drag.offsetHeight), mxTop);
對于左邊上邊要取更大的值,對于右邊下面就要取更小的值。
【容器范圍限制】
容器范圍限制的意思就是把范圍限制在一個容器_mxContainer內。
要注意的是拖放對象必須包含在_mxContainer中,因為程序中是使用相對定位來設置容器范圍限制的(如果是在容器外就要用絕對定位,這樣處理就比較麻煩了),還有就是容器空間要比拖放對象大,這個就不用說明了吧。
原理跟固定范圍限制差不多,只是范圍參數是根據容器的屬性的設置的。
當設置了容器,在Repair程序會自動把position設為relative來相對定位:
!this._mxContainer || CurrentStyle(this._mxContainer).position == "relative" || (this._mxContainer.style.position = "relative");
ps:其中CurrentStyle是用來獲取最終樣式(詳細看這里的最終樣式部分)。
注意如果在程序執行之前設置過拖放對象的left和top而容器沒有設置relative,在自動設置relative時會發生移位現象,所以程序在初始化時就執行一次Repair程序防止這種情況。因為offsetLeft和offsetTop要在設置relative之前獲取才能正確獲取值,所以在Start程序中Repair要在設置_x和_y之前執行。
由于是相對定位,對于容器范圍來說范圍參數上下左右的值分別是0、clientHeight、0、clientWidth。
clientWidth和clientHeight是容器可視部分的寬度和高度(詳細參考這里)。
為了容器范圍能兼容固定范圍的參數,程序中會獲取容器范圍和固定范圍中范圍更小的值:
Code
mxLeft = Math.max(mxLeft, 0);
mxTop = Math.max(mxTop, 0);
mxRight = Math.min(mxRight, this._mxContainer.clientWidth);
mxBottom = Math.min(mxBottom, this._mxContainer.clientHeight);
因為設置相對定位的關系,容器_mxContainer設置過后一般不要取消或修改,否則很容易造成移位異常。
【鼠標捕獲】
我在一個拖放實例中看到,即使鼠標移動到瀏覽器外面,拖放程序依然能夠執行,仔細查看后發現是用了setCapture。
鼠標捕獲(setCapture)是這個程序的重點,作用是將鼠標事件捕獲到當前文檔的指定的對象。這個對象會為當前應用程序或整個系統接收所有鼠標事件。
使用很簡單:
this._Handle.setCapture();
setCapture捕獲以下鼠標事件:onmousedown、onmouseup、onmousemove、onclick、ondblclick、onmouseover和onmouseout。
程序中主要是要捕獲onmousemove和onmouseup事件。
msdn的介紹中還說到setCapture有一個bool參數,用來設置在容器內的鼠標事件是否都被容器捕獲。
容器就是指調用setCapture的對象,大概意思就是:
參數為true時(默認)容器會捕獲容器內所有對象的鼠標事件,即容器內的對象不會觸發鼠標事件(跟容器外的對象一樣);
參數為false時容器不會捕獲容器內對象的鼠標事件,即容器內的對象可以正常地觸發事件和取消冒泡。
而對于容器外的鼠標事件無論參數是什么都會被捕獲,可以用下面這個簡單的例子測試一下(ie):
Code
<html>
<body onclick="alert(2)">
<div onmousemove="alert(1)">mouseover</div>
<script>document.body.setCapture();</script>
</body>
</html>
這里的參數是true,一開始body會捕獲所有鼠標事件,即使鼠標經過div也不會觸發onmousemove事件。
換成false的話,div就可以捕獲鼠標事件,就能觸發div的onmousemove事件了。
拖放結束后還要使用releaseCapture釋放鼠標,這個可以放在Stop程序中:
this._Handle.releaseCapture();
setCapture是ie的鼠標捕獲方法,對于ff也有對應的captureEvents和releaseEvents方法。
但這兩個方法只能由window來調用,而且muxrwc說這兩個方法在DOM2里已經廢棄了,在ff里已經沒用了。
不過ff里貌似會自動設置取消鼠標捕獲,但具體的情形就不清楚了,找不到一個比較詳細的介紹,誰有這方面的資料記得告訴我啊。
下面都是我的猜測,ff的鼠標捕獲相當于能自動設置和釋放的document.body.setCapture(false)。
因為我測試下面的程序,發現ie和ff效果是差不多的:
ie:
Code
<html>
<body>
<div id="aa" onmouseover="alert(1)"></div>
<script>
document.body.onmousedown=function(){this.setCapture(false)}
document.body.onmouseup=function(){this.releaseCapture()}
document.onmousemove=function(){aa.innerHTML+=1}
</script>
</body>
</html>
ff:
Code
<html>
<body>
<div id="aa" onmouseover="alert(1)"></div>
<script>
document.onmousemove=function(){aa.innerHTML+=1}
</script>
</body>
</html>
可惜沒有權威的資料參考就只能猜猜了,還有很多還沒有理解的地方以后再研究拉。
注意ff2下的鼠標捕獲有一個bug,當拖放對象內部沒有文本內容并拖放到瀏覽器外時捕獲就會失效。
給拖放對象插入一個空文本,例如<font size='1px'> </font>就可以解決,不過這個bug在ff3已經修正了。
【焦點丟失】
一般情況下,鼠標捕獲都能正常捕獲事件,但如果瀏覽器窗口的焦點丟失就會導致捕獲失效。
我暫時測試到會導致焦點丟失的操作包括切換窗口(包括alt+tab),alert和popup等彈出窗體。
當焦點丟失時應該同時執行Stop程序結束拖放,但當焦點丟失就不能捕獲mouseup事件也就是不能觸發_fS。
還好ie有onlosecapture事件會在捕獲失效時觸發,針對這個情況可以這樣設置:
addEventHandler(this._Handle, "losecapture", this._fS);
并在Stop程序中移除:
removeEventHandler(this._Handle, "losecapture", this._fS);
但ff沒有類似的方法,不過muxrwc找到一個替代losecapture的window.onblur事件,那么可以在Start程序中設置:
addEventHandler(window, "blur", this._fS);
在Stop程序中移除:
removeEventHandler(window, "blur", this._fS);
那ie也有window.onblur事件,用window.onblur代替losecapture不就可以省一段代碼了嗎。
接著我做了一些測試,發現基本上觸發losecapture的情況都會同時觸發window.onblur,看來真的可以。
于是我修改程序用window.onblur代替losecapture,但測試后就出問題了,我發現如果我用alt+tab切換到另一個窗口,拖動還可以繼續,但這個時候應該是已經丟失焦點了。
于是我逐一排除測試和程序代碼,結果發現如果使用了DTD,那么window.onblur會在再次獲得焦點時才會觸發。
大家可以用下面這段代碼測試:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script>window.onblur=function(){alert(1)}</script>
在切換到其他程序后,再切換回來才會觸發window.onblur,還有幾個比較怪異的狀況就不說了,反正ie用window.onblur是不理想的了。
【取消默認動作】
對選擇狀態的文本內容、連接和圖片等進行拖放操作會觸發系統的默認動作,例如ie中拖動圖片鼠標會變成禁止操作狀態,這樣會導致這個拖放程序執行失敗。
不過ie在設置了setCapture之后,通過用戶界面用鼠標進行拖放操作和內容選擇都會被禁止。
意思就是setCapture之后就不能對文檔內容進行拖放和選擇,注意這里的拖放是指系統的默認動作,例如ondragstart就不會被觸發。
不過如果setCapture的參數是false的話,容器內的對象還是可以觸發事件的(具體看鼠標捕獲部分),所以setCapture的參數要設成true或保留默認值。
而ff的鼠標捕獲沒有這個功能,但可以用preventDefault來取消事件的默認動作來解決:
oEvent.preventDefault();
ps:據說使用preventDefault會出現mouseup丟失的情況,但我在ff3中測試沒有發現,如果各位發現任何mouseup丟失的情況,務必告訴我啊。
【清除選擇】
ie在設置setCapture之后內容選擇都會被禁止,但也因此不會清除在設置之前就已經選擇的內容,而且設置之后也能通過其他方式選擇內容,
例如用ctrl+a來選擇內容。
ps:onkeydown、onkeyup和onkeypress事件不會受到鼠標捕獲影響。
而ff在mousedown時就能清除原來選擇的內容,但拖動鼠標,ctrl+a時還是會繼續選擇內容。
不過在取消了系統默認動作之后,這樣的選擇并不會對拖放操作造成影響,這里設置主要還是為了更好的體驗。
以前我用禁止拖放對象被選擇的方法來達到目的,即ie中設置拖放對象的onselectstart返回false,在ff中設置樣式MozUserSelect(css:-moz-user-select)為none。
但這種方法只能禁止拖放對象本身被選擇,后來找到個更好的方法清除選擇,不但不影響拖放對象的選擇效果,還能對整個文檔進行清除:
ie:document.selection.empty()
ff:window.getSelection().removeAllRanges()
為了防止在拖放過程中選擇內容,所以把它放到Move程序中,下面是兼容的寫法:
window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
【margin】
還有一個情況,當拖放對象設置了margin,那么拖放的時候就會錯位(給SimpleDrag的拖放對象設置margin就可以測試)。
原因是在Start程序設置_x和_y時是使用offset獲取的,而這個值是包括margin的,所以在設置left和top之前要減去這個margin。
但如果在Start程序中就去掉margin那么在Move程序中設置范圍限制時就會計算錯誤,
所以最好是在Start程序中獲取值:
this._marginLeft = parseInt(CurrentStyle(this.Drag).marginLeft) || 0;
this._marginTop = parseInt(CurrentStyle(this.Drag).marginTop) || 0;
在Move程序中設置值:
this.Drag.style.left = iLeft - this._marginLeft + "px";
this.Drag.style.top = iTop - this._marginTop + "px";
要注意margin要在范圍修正之后再設置,否則會錯位。
【透明背景bug】
在ie有一個透明背景bug(不知算不算bug),可以用下面的代碼測試:
Code
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<body>
<div onmousedown="alert(1)" style="border:10px solid #C4E3FD; width:50px; height:50px;position:absolute;"></div>
</body>
</html>
點擊div的背景會觸發不了事件(點擊邊框或div里面的對象是可以觸發的)。
到底什么時候會出現這個bug呢,再用下面的代碼測試:
Code
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html style="background-color:#fff;">
<style>div{height:100px; border:10px solid #000;}</style>
<body style="border:10px solid #FF0000;background-color:#ccc;">
<div style="position:relative;width:150px;">
<div style="border-color:#3F3;width:30px; height:150px;background-color:#ccc; position:absolute;left:0"></div>
<img style="width:40px; height:170px; position:absolute;left:50px" src="1.jpg" temp_src="1.jpg" />
<div onclick="alert(1)" style="border-color:#00F;position:absolute;top:50px; width:200px; left:0"></div>
</div>
</body>
</html>
測試代碼中我把背景顏色(包括body)設成灰色,首先可以看出在藍色div(測試對象)內只要觸發點是在灰色上面,就能觸發事件;相反,在其他不是背景的地方,即使是邊框、圖片,也不能觸發事件。
就像是把灰色的背景的補到藍色div上來,而且僅僅是背景能這樣,多奇怪的設定啊。
這里要說明的是body比較特別,不管背景是不是透明,只要觸發點是直接在body上就能觸發事件。
我的結論是如果觸發事件的對象背景是透明的,而事件的觸發點不在對象內的元素上,也不是直接在body上,而且透明背景外沒有非透明背景的話,那么事件觸發就會失敗。
這個結論寫得不太好,因為我都不知改怎么表達這奇怪的設定,希望各位能明白。
ps:這里設置圖片背景跟顏色背景效果是一樣的。
那最好解決的方法就是給對象設一個非透明背景,但有時需求正好是要透明的,那怎么辦呢?
首先想到的是加上背景色后設置完全透明,但這樣連邊框,容器內的對象等都完全透明了,不夠好。
如果能保證觸發點直接在body或非背景上也可以,如果這個也不能保證呢?
我想到的一個解決方法是在容器里面加一個層,覆蓋整個容器,并設置背景色和完全透明:
Code
with(this._Handle.appendChild(document.createElement("div")).style){
width = height = "100%"; backgroundColor = "#fff"; filter = "alpha(opacity:0)";
}
到這里又不得不說ie6的一個渲染bug,用下面的代碼測試(ie6):
Code
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<body>
<div id="aa" style="width:300px;height:100px;border:1px solid;"><div style="background:#00f;height:100%;"></div></div>
<script>setTimeout("aa.style.height=200",0)</script>
</body>
</html>
可以對比ie7的效果,可以看出里面的div雖然高度設置了100%,但在外面的div修改高度之后,卻不知什么原因沒有填充了。
如果這個div是拖動對象,突然有一半拖不動了,那肯定不行。
還好BlueDestiny告訴我一個解決方法,設置對象的overflow為hidden,里面的div又會自動填充了。
BlueDestiny說“出現這個問題的原因是因為IE6渲染的問題,通過某些CSS的屬性可以讓DOM改變之后再次渲染。”
這個bug也有用zoom解決的,反正就是要使dom再次渲染。
當發現程序有這個bug出現,把程序可選參數Transparent設為true就會自動插入這樣一個層了。
到這里插入一個小知識吧,細心的話會發現上面的測試代碼中我給html設了一個背景色。
大家可以去掉這個背景色,會發現背景色會設置到整個頁面,雖然一直都是用body設置頁面背景色,但現在會不會有一個疑惑,body是紅色框的部分,為什么設置它的背景色就能應用到整個頁面,而給html設置了背景色就又“正常”顯示了呢。
這個可以從CSS21的w3c標準中關于background的部分看到原因:
For HTML documents, however, we recommend that authors specify the background for the BODY element rather than the HTML element. For HTML documents whose root HTML element has computed values of 'transparent' for 'background-color' and 'none' for 'background-image', user agents must instead use the computed value of those properties from that HTML element's first BODY element child when painting backgrounds for the canvas, and must not paint a background for that BODY element. Such backgrounds must also be anchored at the same point as they would be if they were painted only for the root element. This does not apply to XHTML documents.
我英文很爛,就勉強翻譯一下吧:
對于HTML文檔,我們建議作者(這是對瀏覽器的制作者說的)使用BODY元素的background,而不是HTML元素的。如果HTML文檔的根元素(HTML元素)的'background-color'是'transparent',同時'background-image'是'none'(這兩個剛好就是默認值),那么在設置背景時就取HTML元素的第一個BODY子元素的屬性值,并且不再渲染那個元素的background。
后面兩個句就看不太懂了,不過已經足夠解析原因了。
【iframe】
如果頁面上有嵌入iframe,那就要注意了,因為鼠標捕獲在iframe上會有問題。
例如在拖放容器內的一個iframe上快速移動拖放,或者鼠標拖動到容器外的一個iframe上,反正就是鼠標在iframe上(注意沒有其他元素隔開),就會出問題:
首先是捕獲的失效,鼠標在iframe上,就拖不動了,但并不會觸發losecapture,更不用說window的blur了,這個在ie和ff都是一樣;
其次ie里在iframe多摩擦幾次,還可能導致ie死掉(原因不明)。
下面是我想到的幾種方法:
隱藏頁面的iframe,比較簡單,但可能有些在iframe中重要的信息也被隱藏,或者造成頁面布局的錯位,用戶體驗不好;
鼠標移動到iframe后取消拖放,難度不大,但同樣用戶體驗不好;
每個iframe用一個透明的層遮住,很麻煩,要計算好每個iframe的位置和大小;
用一個透明的層把整個頁面遮住,比較推薦,也比較簡單,下面介紹這種做法。
我在仿LightBox內容顯示效果做的那個覆蓋層正好能應用在這里,首先實例化一個透明的覆蓋層:
var ol = new OverLay({ Opacity: 0 });
然后在onStart和onStop事件中添加ol.Show()和ol.Close()來顯示和隱藏覆蓋層就可以了,這樣只要不是在iframe觸發拖放就沒有問題了。
有其他更好的方法也請各位指教。
暫時就研究到這里,想不到小小的拖放就有這么多的學問。
還有滾屏等這些都還沒考慮到呢,等以后有需要了再來研究拉。

使用說明
實例化時只需要一個參數,就是拖放對象:
new SimpleDrag("idDrag");
有以下這些可選參數和屬性:
屬性:默認值//說明
Handle:"",//設置觸發對象(不設置則使用拖放對象)
Limit:false,//是否設置范圍限制(為true時下面參數有用,可以是負數)
mxLeft:0,//左邊限制
mxRight:9999,//右邊限制
mxTop:0,//上邊限制
mxBottom:9999,//下邊限制
mxContainer:"",//指定限制在容器內
LockX:false,//是否鎖定水平方向拖放
LockY:false,//是否鎖定垂直方向拖放
Lock:false,//是否鎖定
Transparent: false,//是否透明
onStart:function(){},//開始移動時執行
onMove:function(){},//移動時執行
onStop:function(){}//結束移動時執行
還有屬性Drag是拖放對象,Transparent、Handle和mxContainer初始化后就不能再設置。

程序代碼
Code 
復制代碼 代碼如下:
var isIE = (document.all) ? true : false;
var $ = function (id) {
return "string" == typeof id ? document.getElementById(id) : id;
};
var Class = {
create: function() {
return function() { this.initialize.apply(this, arguments); }
}
}
var Extend = function(destination, source) {
for (var property in source) {
destination[property] = source[property];
}
}
var Bind = function(object, fun) {
return function() {
return fun.apply(object, arguments);
}
}
var BindAsEventListener = function(object, fun) {
return function(event) {
return fun.call(object, (event || window.event));
}
}
var CurrentStyle = function(element){
return element.currentStyle || document.defaultView.getComputedStyle(element, null);
}
function addEventHandler(oTarget, sEventType, fnHandler) {
if (oTarget.addEventListener) {
oTarget.addEventListener(sEventType, fnHandler, false);
} else if (oTarget.attachEvent) {
oTarget.attachEvent("on" + sEventType, fnHandler);
} else {
oTarget["on" + sEventType] = fnHandler;
}
};
function removeEventHandler(oTarget, sEventType, fnHandler) {
if (oTarget.removeEventListener) {
oTarget.removeEventListener(sEventType, fnHandler, false);
} else if (oTarget.detachEvent) {
oTarget.detachEvent("on" + sEventType, fnHandler);
} else {
oTarget["on" + sEventType] = null;
}
};
//拖放程序
var Drag = Class.create();
Drag.prototype = {
//拖放對象
initialize: function(drag, options) {
this.Drag = $(drag);//拖放對象
this._x = this._y = 0;//記錄鼠標相對拖放對象的位置
this._marginLeft = this._marginTop = 0;//記錄margin
//事件對象(用于綁定移除事件)
this._fM = BindAsEventListener(this, this.Move);
this._fS = Bind(this, this.Stop);
this.SetOptions(options);
this.Limit = !!this.options.Limit;
this.mxLeft = parseInt(this.options.mxLeft);
this.mxRight = parseInt(this.options.mxRight);
this.mxTop = parseInt(this.options.mxTop);
this.mxBottom = parseInt(this.options.mxBottom);
this.LockX = !!this.options.LockX;
this.LockY = !!this.options.LockY;
this.Lock = !!this.options.Lock;
this.onStart = this.options.onStart;
this.onMove = this.options.onMove;
this.onStop = this.options.onStop;
this._Handle = $(this.options.Handle) || this.Drag;
this._mxContainer = $(this.options.mxContainer) || null;
this.Drag.style.position = "absolute";
//透明
if(isIE && !!this.options.Transparent){
//填充拖放對象
with(this._Handle.appendChild(document.createElement("div")).style){
width = height = "100%"; backgroundColor = "#fff"; filter = "alpha(opacity:0)";
}
}
//修正范圍
this.Repair();
addEventHandler(this._Handle, "mousedown", BindAsEventListener(this, this.Start));
},
//設置默認屬性
SetOptions: function(options) {
this.options = {//默認值
Handle: "",//設置觸發對象(不設置則使用拖放對象)
Limit: false,//是否設置范圍限制(為true時下面參數有用,可以是負數)
mxLeft: 0,//左邊限制
mxRight: 9999,//右邊限制
mxTop: 0,//上邊限制
mxBottom: 9999,//下邊限制
mxContainer: "",//指定限制在容器內
LockX: false,//是否鎖定水平方向拖放
LockY: false,//是否鎖定垂直方向拖放
Lock: false,//是否鎖定
Transparent: false,//是否透明
onStart: function(){},//開始移動時執行
onMove: function(){},//移動時執行
onStop: function(){}//結束移動時執行
};
Extend(this.options, options || {});
},
//準備拖動
Start: function(oEvent) {
if(this.Lock){ return; }
this.Repair();
//記錄鼠標相對拖放對象的位置
this._x = oEvent.clientX - this.Drag.offsetLeft;
this._y = oEvent.clientY - this.Drag.offsetTop;
//記錄margin
this._marginLeft = parseInt(CurrentStyle(this.Drag).marginLeft) || 0;
this._marginTop = parseInt(CurrentStyle(this.Drag).marginTop) || 0;
//mousemove時移動 mouseup時停止
addEventHandler(document, "mousemove", this._fM);
addEventHandler(document, "mouseup", this._fS);
if(isIE){
//焦點丟失
addEventHandler(this._Handle, "losecapture", this._fS);
//設置鼠標捕獲
this._Handle.setCapture();
}else{
//焦點丟失
addEventHandler(window, "blur", this._fS);
//阻止默認動作
oEvent.preventDefault();
};
//附加程序
this.onStart();
},
//修正范圍
Repair: function() {
if(this.Limit){
//修正錯誤范圍參數
this.mxRight = Math.max(this.mxRight, this.mxLeft + this.Drag.offsetWidth);
this.mxBottom = Math.max(this.mxBottom, this.mxTop + this.Drag.offsetHeight);
//如果有容器必須設置position為relative來相對定位,并在獲取offset之前設置
!this._mxContainer || CurrentStyle(this._mxContainer).position == "relative" || (this._mxContainer.style.position = "relative");
}
},
//拖動
Move: function(oEvent) {
//判斷是否鎖定
if(this.Lock){ this.Stop(); return; };
//清除選擇
window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
//設置移動參數
var iLeft = oEvent.clientX - this._x, iTop = oEvent.clientY - this._y;
//設置范圍限制
if(this.Limit){
//設置范圍參數
var mxLeft = this.mxLeft, mxRight = this.mxRight, mxTop = this.mxTop, mxBottom = this.mxBottom;
//如果設置了容器,再修正范圍參數
if(!!this._mxContainer){
mxLeft = Math.max(mxLeft, 0);
mxTop = Math.max(mxTop, 0);
mxRight = Math.min(mxRight, this._mxContainer.clientWidth);
mxBottom = Math.min(mxBottom, this._mxContainer.clientHeight);
};
//修正移動參數
iLeft = Math.max(Math.min(iLeft, mxRight - this.Drag.offsetWidth), mxLeft);
iTop = Math.max(Math.min(iTop, mxBottom - this.Drag.offsetHeight), mxTop);
}
//設置位置,并修正margin
if(!this.LockX){ this.Drag.style.left = iLeft - this._marginLeft + "px"; }
if(!this.LockY){ this.Drag.style.top = iTop - this._marginTop + "px"; }
//附加程序
this.onMove();
},
//停止拖動
Stop: function() {
//移除事件
removeEventHandler(document, "mousemove", this._fM);
removeEventHandler(document, "mouseup", this._fS);
if(isIE){
removeEventHandler(this._Handle, "losecapture", this._fS);
this._Handle.releaseCapture();
}else{
removeEventHandler(window, "blur", this._fS);
};
//附加程序
this.onStop();
}
};

測試代碼:
Code
復制代碼 代碼如下:
<style>
#idContainer{ border:10px solid #990000; width:600px; height:300px;}
#idDrag{ border:5px solid #C4E3FD; background:#C4E3FD; width:50px; height:50px; top:50px; left:50px;}
#idHandle{cursor:move; height:25px; background:#0000FF; overflow:hidden;}
</style>
<div id="idContainer">
<div id="idDrag"><div id="idHandle"></div></div>
</div>
<input id="idReset" type="button" value="復位" />
<input id="idLock" type="button" value="鎖定" />
<input id="idLockX" type="button" value="鎖定水平" />
<input id="idLockY" type="button" value="鎖定垂直" />
<input id="idLimit" type="button" value="范圍鎖定" />
<input id="idLimitOff" type="button" value="取消范圍鎖定" />
<br />拖放狀態:<span id="idShow">未開始</span>
<script>
var drag = new Drag("idDrag", { mxContainer: "idContainer", Handle: "idHandle", Limit: true,
onStart: function(){ $("idShow").innerHTML = "開始拖放"; },
onMove: function(){ $("idShow").innerHTML = "left:"+this.Drag.offsetLeft+";top:"+this.Drag.offsetTop; },
onStop: function(){ $("idShow").innerHTML = "結束拖放"; }
});
$("idReset").onclick = function(){
drag.Limit = true;
drag.mxLeft = drag.mxTop = 0;
drag.mxRight = drag.mxBottom = 9999;
drag.LockX = drag.LockY = drag.Lock = false;
}
$("idLock").onclick = function(){ drag.Lock = true; }
$("idLockX").onclick = function(){ drag.LockX = true; }
$("idLockY").onclick = function(){ drag.LockY = true; }
$("idLimit").onclick = function(){ drag.mxRight = drag.mxBottom = 200;drag.Limit = true; }
$("idLimitOff").onclick = function(){ drag.Limit = false; }
</script>


[Ctrl+A 全選 注:如需引入外部Js需刷新才能執行]

JavaScript技術JavaScript 拖放效果代碼,轉載需保留來源!

鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。

主站蜘蛛池模板: 国产精品青草久久福利不卡 | 国产一二三四区在线观看 | 色综合久久88中文字幕 | 5252色欧美在线男人的天堂 | 91中文字幕在线 | 激情在线观看视频免费的 | 色涩网站在线观看 | 天天天色综合 | 六月丁香激情 | 国产精彩对白综合视频 | 久久一本精品久久精品66 | 78m.78摸免费视频 | 福利视频免费看 | 在线观看色网站 | 成年黄网站 | 99久久精品国产亚洲 | 97成人免费视频 | 国产91在线视频观看 | 国产夜色 | 男女免费爽爽爽在线视频 | 久久黄色影片 | 四虎在线视频免费观看视频 | 91欧美亚洲 | 成人中文字幕一区二区三区 | 好吊妞视频在线观看 | 日本亚洲网站 | 99999久久久久久亚洲 | 2021久久精品免费观看 | 四虎影视亚洲精品 | 日韩中文字幕免费版 | 国产成人精品日本亚洲网址 | 色www情| 精品久久久久免费极品大片 | 91精品国产91热久久p | 五月婷婷丁香六月 | 激情五月综合网 | 精产国品一二二区视 | 91久久国产成人免费观看资源 | 国产一区二区三区毛片 | 国产精品资源网站在线观看 | 91杏吧在线网站 |