日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

JavaScript拖放效果分析

瀏覽:36日期:2023-12-16 10:06:35

拖放效果,也叫拖拽,學名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要設置的值使拖放對象能保持在設置的范圍內。

固定范圍限制

容器范圍限制就是指定上下左右的拖放范圍。各個屬性的意思是:

上(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中,因為程序中是使用相對定位來設置容器范圍限制的(如果是在容器外就要用絕對定位,這樣處理就比較麻煩了),還有就是容器空間要比拖放對象大,這個就不用說明了吧。原理跟固定范圍限制差不多,只是范圍參數是根據容器的屬性的設置的。

當設置了容器,會自動把position設為relative來相對定位:

!this._mxContainer || CurrentStyle(this._mxContainer).position == “relative” || (this._mxContainer.style.position = “relative”);

注意relative要在獲取offsetLeft和offsetTop即設置_x和_y之前設置,offset才能正確獲取值。

由于是相對定位,對于容器范圍來說范圍參數上下左右的值分別是0、clientHeight、0、clientWidth。

clientWidth和clientHeight是容器可視部分的寬度和高度(詳細參考這里)。為了容器范圍能兼容固定范圍的參數,程序中會獲取容器范圍和固定范圍中范圍更小的值:

mxLeft = Math.max(mxLeft, 0);mxTop = Math.max(mxTop, 0);mxRight = Math.min(mxRight, this._mxContainer.clientWidth);mxBottom = Math.min(mxBottom, this._mxContainer.clientHeight);

注意如果在程序執行之前設置過拖放對象的left和top而容器沒有設置relative,在自動設置relative時會發生移位現象,所以程序在初始化時就執行一次Repair程序防止這種情況。因為offsetLeft和offsetTop要在設置relative之前獲取才能正確獲取值,所以在Start程序中Repair要在設置_x和_y之前執行。

因為設置相對定位的關系,容器_mxContainer設置過后一般不要取消或修改,否則很容易造成移位異常。

鼠標捕獲

我在一個拖放實例中看到,即使鼠標移動到瀏覽器外面,拖放程序依然能夠執行,仔細查看后發現是用了setCapture。鼠標捕獲(setCapture)是這個程序的重點,作用是將鼠標事件捕獲到當前文檔的指定的對象。這個對象會為當前應用程序或整個系統接收所有鼠標事件。使用很簡單:

this._Handle.setCapture();

setCapture捕獲以下鼠標事件:onmousedown、onmouseup、onmousemove、onclick、ondblclick、onmouseover和onmouseout。程序中主要是要捕獲onmousemove和onmouseup事件。msdn的介紹中還說到setCapture有一個bool參數,用來設置在容器內的鼠標事件是否都被容器捕獲。容器就是指調用setCapture的對象,大概意思就是:參數為true時(默認)容器會捕獲容器內所有對象的鼠標事件,即容器內的對象不會觸發鼠標事件(跟容器外的對象一樣);參數為false時容器不會捕獲容器內對象的鼠標事件,即容器內的對象可以正常地觸發事件和取消冒泡。而對于容器外的鼠標事件無論參數是什么都會被捕獲,可以用下面這個簡單的例子測試一下(ie):

<html><body onclick=”alert(2)”><div onmousemove=”alert(1)”>mouseover </div><script>document.body.setCapture(); </script></body></html>

這里的參數是true,一開始body會捕獲所有鼠標事件,即使鼠標經過div也不會觸發onmousemove事件。換成false的話,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:

<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:

<html><body><div id=”aa” onmouseover=”alert(1)”> </div><script>document.onmousemove=function(){aa.innerHTML+=1}</script></body></html>

可惜沒有權威的資料參考就只能猜猜了,還有很多還沒有理解的地方以后再研究拉。

注意ff2下的鼠標捕獲有一個bug,當拖放對象內部沒有文本內容并拖放到瀏覽器外時捕獲就會失效。給拖放對象插入一個空文本,例如 <font size=’1px’>&nbsp; </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;

其中CurrentStyle是用來獲取最終樣式,詳細看這里的最終樣式部分。

在Move程序中設置值:

this.Drag.style.left = iLeft - this._marginLeft + “px”;this.Drag.style.top = iTop - this._marginTop + “px”;

要注意margin要在范圍修正只后再設置,否則會錯位。

【透明背景bug】

在ie有一個透明背景bug(不知算不算bug),可以用下面的代碼測試:

<!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>

會發現背景點擊觸發不了事件,不過點擊邊框的話還是可以觸發。為什么呢?再用下面的代碼測試:

<!DOCTYPE html PUBLIC “//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd“><html><body style=”border:1px solid #FF0000;”><style>div{width:100px; height:100px; border:1px solid #000;} </style><div style=”position:relative;”><div onclick=”alert(1)” style=”border-color:#00f;margin:50px;”> </div><div onclick=”alert(2)” style=”border-color:#6f0;position:absolute;top:50px;”> </div></div></body></html>

應該能看出個大概了,下面兩個div超出body(即超出紅色框)的部分就觸發不了事件。也就是說當觸發事件的點,在body以外,而背景又是透明的,那么就會誤認為觸發點是在了body外空白的地方,所以觸發不了事件。那解決的方法就是,使事件觸發點保持在body內,或者設置一個非透明背景。

那程序中只要給拖放對象設一個背景色就可以解決了,但有時需求正好是要透明(例如切割效果),那怎么辦呢?首先想到的是加上背景色后設置完全透明,但這樣連邊框,容器內的對象等都完全透明了,這個不好。我想到的一個解決方法是在容器里面加一個層,覆蓋整個容器,并設置背景色和完全透明:

with(this._Handle.appendChild(document.createElement(”div”)).style){width = height = “100%”; backgroundColor = “#fff”; filter = “alpha(opacity:0)”;}

當發現程序有這個bug出現,把程序可選參數Transparent設為true就會自動插入這樣一個層了。各位如果有更好的方法請多多指點。

暫時就研究到這里,不過還有iframe,滾屏等這些還沒考慮到,等以后有需要了再來研究拉。

標簽: JavaScript
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
国产伦久视频在线观看| 精品精品99| 婷婷久久一区| 极品日韩av| 在线精品视频在线观看高清| 999久久久91| 欧美日韩国产探花| 国产毛片久久| 视频精品一区二区| 五月天久久网站| 国产一级一区二区| 日韩中文字幕一区二区高清99| 青草久久视频| 欧美激情三区| 黑人精品一区| 图片区亚洲欧美小说区| 亚洲精选成人| 国产伦精品一区二区三区在线播放| 国产免费播放一区二区| 欧美黑人做爰爽爽爽| 黄毛片在线观看| 在线国产一区| 91精品国产自产观看在线| 精品欧美视频| 日韩一区三区| 视频一区视频二区中文字幕| 久久国产免费看| 天堂av在线| 视频在线观看一区二区三区| 久久国产欧美日韩精品| 首页国产精品| 国产精品免费看| 97久久超碰| 午夜精品成人av| 亚洲资源网站| 91亚洲国产成人久久精品| 亚洲国产一区二区在线观看| 国产午夜一区| 亚洲电影有码| 蜜臀国产一区二区三区在线播放 | av在线最新| 美女尤物久久精品| 久久久国产精品网站| 99国产精品私拍| 国产精久久久| 香蕉成人久久| 精品久久久网| 国产免费成人| 久久久久久自在自线| 亚洲精品视频一二三区| 久久久久久一区二区| 蜜桃视频在线观看一区| 免费在线观看一区| 亚洲激情不卡| 欧美激情久久久久久久久久久| 精品一区三区| 欧美精品二区| 亚洲激情黄色| 97精品中文字幕| 四虎在线精品| 999久久久免费精品国产| 国产精品一区二区中文字幕| 欧美天堂亚洲电影院在线观看| 国产精品99久久久久久董美香| 在线亚洲激情| av高清不卡| 欧美日韩1区2区3区| 青青久久av| 国产精品sss在线观看av| 在线亚洲观看| 久久天堂成人| 精品五月天堂| 欧美在线观看天堂一区二区三区| 影音先锋久久| 亲子伦视频一区二区三区| 久久99偷拍| 日本亚洲不卡| 9色国产精品| 九九精品调教| 久久精品网址| 91国内精品| 免费一级片91| 色天使综合视频| 精品欧美日韩精品| 国产欧美日韩一区二区三区四区| 国产精品毛片在线| 久久亚洲国产| 麻豆视频在线观看免费网站黄| 国产欧美69| 日韩精品免费视频一区二区三区| 在线成人直播| 亚洲一级影院| 91欧美在线| 精品久久久久久久| 国产欧美一区二区色老头| 亚洲v天堂v手机在线| 久久高清国产| 久久国产电影| 日韩中文欧美| av中文资源在线资源免费观看| 久久免费视频66| 麻豆成人在线观看| 久久先锋影音| 视频在线观看一区| 一区二区三区四区在线观看国产日韩| 不卡一区2区| 久久国产亚洲精品| 日韩福利一区| 色偷偷色偷偷色偷偷在线视频| 精品国产黄a∨片高清在线| 国产精品日韩精品中文字幕| 国产日韩一区二区三区在线播放| 亚洲精品日韩久久| 亚洲三级观看| 日韩三级一区| 91免费精品国偷自产在线在线| 日韩国产欧美视频| 日韩精品欧美大片| 欧美在线91| 国产精品丝袜在线播放| 国产精品任我爽爆在线播放| 欧美三级第一页| 国产精品网站在线看| 麻豆国产精品777777在线| 精品久久网站| 欧美aa一级| 欧美午夜精品一区二区三区电影| 99精品视频在线| 好吊日精品视频| 只有精品亚洲| 欧美一区自拍| 久久精品亚洲| 色天使综合视频| 日韩视频一区二区三区在线播放免费观看| 久久xxxx精品视频| 日本aⅴ免费视频一区二区三区| 国产一卡不卡| 岛国av在线网站| 亚洲大全视频| 亚洲精品自拍| 狂野欧美性猛交xxxx| 国产精品亚洲一区二区三区在线观看| 日韩a一区二区| 久久久久久久久99精品大| 久久久久久黄| 日韩一区二区免费看| 日韩激情综合| 成人在线超碰| 黄色不卡一区| 日本91福利区| 91综合视频| 老司机精品久久| 久久国产婷婷国产香蕉| 97人人精品| 三级欧美在线一区| 国产精品男女| 性欧美xxxx免费岛国不卡电影| 欧美日韩免费观看一区=区三区| 亚洲欧美网站在线观看| 久久wwww| 黄色免费成人| 国产亚洲高清在线观看| av在线最新| 蜜桃av一区二区三区电影| 国产欧美日韩视频在线| 亚洲天堂免费电影| 少妇精品久久久一区二区| 成人在线视频免费| 亚洲一区二区成人| 国产精品久久久久久av公交车| 日韩一区二区三区免费| 免费久久精品视频| 麻豆成人91精品二区三区| 欧美在线观看视频一区| 日本欧美一区二区| 日韩在线二区| 日韩av电影一区| 国产91精品对白在线播放| 日韩高清三区| 久久九九国产| 国产精品xxx| 国产视频一区欧美| 精品一区二区三区中文字幕视频| 在线日韩电影| 国产美女撒尿一区二区| 成人av二区| 欧美交a欧美精品喷水| 免费视频亚洲| 欧美午夜三级| 国产日韩综合| av在线资源| 国产丝袜一区| 久久福利影视| 亚洲综合电影| 欧美片网站免费| 丝袜美腿亚洲一区二区图片| 日产精品一区二区| 91p九色成人| 国产精品日韩久久久| 97精品一区|