拖放效果,也叫拖拽,學名Drag-and-drop ,是最常見的js特效之一。
如果忽略很多細節,實現起來很簡單,但往往細節才是難點所在。
這個程序的原型是在做圖片切割效果的時候做出來的,那時參考了好幾個同類的效果,跟muxrwc和BlueDestiny學習了不少東西。
雖然每次整理都覺得很好了,不過每隔一段時間又會發現得某個地方可以改善,某個地方有錯誤,某些需求需要實現,就像自己學習的知識那樣。
這裡考慮到有的人可能只需要簡單的拖放,所以有一個 簡化版的拖放SimpleDrag ,方便學習。
程序原理
這裡以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 = ...; }
觸發對象
觸發對象是用來觸發拖放程序的。有的時候不需要整個拖放對象都用來觸發,這時就需要觸發對象了。
使用了觸發對象後,進行移動的還是拖放對象,只是用觸發對象來觸發拖放(一般的使用是把觸發對象放到拖放對象裡面)。
范圍限制
要設置范圍限制必須先把Limit設為true。范圍限制分兩種,分別是固定范圍和容器范圍限制,主要在Move程序中設置。
原理是當比較的值超過范圍時,修正left和top要設置的值使拖放對象能保持在設置的范圍內。
固定范圍限制
容器范圍限制就是指定上下左右的拖放范圍。
各個屬性的意思是:
如果范圍設置不正確,可能導致上下或左右同時超過范圍的情況,程序中有一個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 = Ma