|
出處:http://www.blogJava.NET/iamtin/archive/2006/04/27/43668.html
代碼:http://www.blogJava.NET/Files/iamtin/google_drag.rar
復(fù)制代碼 代碼如下:
// 工具類,使用Util的命名空間,方便管理
var Util = new Object();
// 獲取http header里面的UserAgent,瀏覽器信息
Util.getUserAgent = navigator.userAgent;
// 是否是Gecko核心的Browser,比如Mozila、Firefox
Util.isGecko = Util.getUserAgent.indexOf( " Gecko " ) != - 1 ;
// 是否是Opera
Util.isOpera = Util.getUserAgent.indexOf( " Opera " ) != - 1 ;
// 獲取一個element的offset信息,其實(shí)就是相對于Body的padding以內(nèi)的絕對坐標(biāo)
// 后面一個參數(shù)如果是true則獲取offsetLeft,false則是offsetTop
// 關(guān)于offset、style、client等坐標(biāo)的定義參考dindin的這個帖子:http://www.jroller.com/page/dindin/?anchor=pro_Javascript_12
Util.getOffset = function (el, isLeft) {
var retValue = 0 ;
while (el != null ) {
retValue += el[ " offset " + (isLeft ? " Left " : " Top " )];
el = el.offsetParent;
}
return retValue;
};
// 將一個function(參數(shù)中的funcName是這個fuction的名字)綁定到一個element上,并且以這個element的上下文運(yùn)行,其實(shí)是一種繼承,這個可以google些文章看看
Util.bindFunction = function (el, fucName) {
return function () {
return el[fucName].apply(el, arguments);
};
};
// 重新計算所有的可以拖拽的element的坐標(biāo),對同一個column下面的可拖拽圖層重新計算它們的高度而得出新的坐標(biāo),防止遮疊
// 計算出來的坐標(biāo)記錄在pagePosLeft和pagePosTop兩個屬性里面
Util.re_calcOff = function (el) {
for ( var i = 0 ; i < Util.dragArray.length; i ++ ) {
var ele = Util.dragArray[i];
ele.elm.pagePosLeft = Util.getOffset(ele.elm, true );
ele.elm.pagePosTop = Util.getOffset(ele.elm, false );
}
var nextSib = el.elm.nextSibling;
while (nextSib) {
nextSib.pagePosTop -= el.elm.offsetHeight;
nextSib = nextSib.nextSibling;
}
};
// 隱藏Google Ig中間那個table,也就是拖拽的容器,配合show一般就是刷新用,解決一些瀏覽器的怪癖
Util.hide = function () {
Util.rootElement.style.display = " none " ;
};
// 顯示Google Ig中間那個table,解釋同上
Util.show = function () {
Util.rootElement.style.display = "" ;
};
// 移動時顯示的占位虛線框
ghostElement = null ;
// 獲取這個虛線框,通過dom動態(tài)生成
getGhostElement = function () {
if ( ! ghostElement) {
ghostElement = document.createElement( " DIV " );
ghostElement.className = " modbox " ;
ghostElement.backgroundColor = "" ;
ghostElement.style.border = " 2px dashed #aaa " ;
ghostElement.innerHTML = " " ;
}
return ghostElement;
};
// 初始化可以拖拽的Element的函數(shù),與拖拽無關(guān)的我去掉了
function draggable(el) {
// 公用的開始拖拽的函數(shù)
this ._dragStart = start_Drag;
// 公用的正在拖拽的函數(shù)
this ._drag = when_Drag;
// 公用的拖拽結(jié)束的函數(shù)
this ._dragEnd = end_Drag;
// 這個函數(shù)主要用來進(jìn)行拖拽結(jié)束后的dom處理
this ._afterDrag = after_Drag;
// 是否正在被拖動,一開始當(dāng)然沒有被拖動
this .isDragging = false ;
// 將這個Element的this指針注冊在elm這個變量里面,方便在自己的上下文以外調(diào)用自己的函數(shù)等,很常用的方法
this .elm = el;
// 觸發(fā)拖拽的Element,在這里就是這個div上顯示標(biāo)題的那個div
this .header = document.getElementById(el.id + " _h " );
// 對于有iframe的element拖拽不同,這里檢測一下并記錄
this .hasIFrame = this .elm.getElementsByTagName( " IFRAME " ).length > 0 ;
// 如果找到了header就綁定drag相關(guān)的event
if ( this .header) {
// 拖拽時的叉子鼠標(biāo)指針
this .header.style.cursor = " move " ;
// 將函數(shù)綁定到header和element的this上,參照那個函數(shù)的說明
Drag.init( this .header, this .elm);
// 下面三個語句將寫好的三個函數(shù)綁定給這個elemnt的三個函數(shù)鉤子上,也就實(shí)現(xiàn)了element從draggable繼承可拖拽的函數(shù)
this .elm.onDragStart = Util.bindFunction( this , " _dragStart " );
this .elm.onDrag = Util.bindFunction( this , " _drag " );
this .elm.onDragEnd = Util.bindFunction( this , " _dragEnd " );
}
};
// 下面就是draggable里面用到的那4個function
// 公用的開始拖拽的函數(shù)
function start_Drag() {
// 重置坐標(biāo),實(shí)現(xiàn)拖拽以后自己的位置馬上會被填充的效果
Util.re_calcOff( this );
// 記錄原先的鄰居節(jié)點(diǎn),用來對比是否被移動到新的位置
this .origNextSibling = this .elm.nextSibling;
// 獲取移動的時候那個灰色的虛線框
var _ghostElement = getGhostElement();
// 獲取正在移動的這個對象的高度
var offH = this .elm.offsetHeight;
if (Util.isGecko) {
// 修正gecko引擎的怪癖吧
offH -= parseInt(_ghostElement.style.borderTopWidth) * 2 ;
}
// 獲取正在移動的這個對象的寬度
var offW = this .elm.offsetWidth;
// 獲取left和top的坐標(biāo)
var offLeft = Util.getOffset( this .elm, true );
var offTop = Util.getOffset( this .elm, false );
// 防止閃爍,現(xiàn)隱藏
Util.hide();
// 將自己的寬度記錄在style屬性里面
this .elm.style.width = offW + " px " ;
// 將那個灰框設(shè)定得與正在拖動的對象一樣高,比較形象
_ghostElement.style.height = offH + " px " ;
// 把灰框放到這個對象原先的位置上
this .elm.parentNode.insertBefore(_ghostElement, this .elm.nextSibling);
// 由于要拖動必須將被拖動的對象從原先的盒子模型里面抽出來,所以設(shè)定position為absolute,這個可以參考一下css布局方面的知識
this .elm.style.position = " absolute " ;
// 設(shè)置zIndex,讓它處在最前面一層,當(dāng)然其實(shí)zIndex=100是讓它很靠前,如果頁面里有zIndex>100的,那……
this .elm.style.zIndex = 100 ;
// 由于position=absolute了,所以left和top實(shí)現(xiàn)絕對坐標(biāo)定位,這就是先前計算坐標(biāo)的作用,不讓這個模型亂跑,要從開始拖動的地方開始移動
this .elm.style.left = offLeft + " px " ;
this .elm.style.top = offTop + " px " ;
// 坐標(biāo)設(shè)定完畢,可以顯示了,這樣就不會閃爍了
Util.show();
// 這里本來有個ig_d.G,沒搞明白干什么用的,不過沒有也可以用,誰知道麻煩告訴我一聲,不好意思
// 還沒有開始拖拽,這里做個記號
this .isDragging = false ;
return false ;
};
// 在拖拽時的相應(yīng)函數(shù),由于綁定到鼠標(biāo)的move這個event上,所以會傳入鼠標(biāo)的坐標(biāo)clientX, clientY
function when_Drag(clientX, clientY) {
// 剛開始拖拽的時候?qū)D層變透明,并標(biāo)記為正在被拖拽
if ( ! this .isDragging) {
this .elm.style.filter = " alpha(opacity=70) " ;
this .elm.style.opacity = 0.7 ;
this .isDragging = true ;
}
// 被拖拽到的新的column(當(dāng)然也可以是原來那個)
var found = null ;
// 最大的距離,可能是防止溢出或者什么bug
var max_distance = 100000000 ;
// 遍歷所有的可拖拽的element,尋找離當(dāng)前鼠標(biāo)坐標(biāo)最近的那個可拖拽元素,以便后面插入
for ( var i = 0 ; i < Util.dragArray.length; i ++ ) {
var ele = Util.dragArray[i];
// 利用勾股定理計算鼠標(biāo)到遍歷到的這個元素的距離
var distance = Math.sqrt(Math.pow(clientX - ele.elm.pagePosLeft, 2 ) + Math.pow(clientY - ele.elm.pagePosTop, 2 ));
// 自己已經(jīng)浮動了,所以不計算自己的
if (ele == this ) {
continue ;
}
// 如果計算失敗繼續(xù)循環(huán)
if (isNaN(distance)) {
continue ;
}
// 如果更小,記錄下這個距離,并將它作為found
if (distance < max_distance) {
max_distance = distance;
found = ele;
}
}
// 準(zhǔn)備讓灰框落腳
var _ghostElement = getGhostElement();
// 如果找到了另外的落腳點(diǎn)
if (found != null && _ghostElement.nextSibling != found.elm) {
// 找到落腳點(diǎn)就先把灰框插進(jìn)去,這就是我們看到的那個灰框停靠的特效,有點(diǎn)像吸附的感覺,哈哈
found.elm.parentNode.insertBefore(_ghostElement, found.elm);
if (Util.isOpera) {
// Opera的現(xiàn)實(shí)問題,要隱藏/顯示后才能刷新出變化
document.body.style.display = " none " ;
document.body.style.display = "" ;
}
}
};
// 拖拽完畢
function end_Drag() {
// 拖拽完畢后執(zhí)行后面的鉤子,執(zhí)行after_Drag(),如果布局發(fā)生了變動了就記錄到遠(yuǎn)程服務(wù)器,保存你拖拽后新的布局順序
if ( this ._afterDrag()) {
// remote call to save the change
}
return true ;
};
// 拖拽后的執(zhí)行鉤子
function after_Drag() {
var returnValue = false ;
// 防止閃爍
Util.hide();
// 把拖拽時的position=absolute和相關(guān)的那些style都消除
this .elm.style.position = "" ;
this .elm.style.width = "" ;
this .elm.style.zIndex = "" ;
this .elm.style.filter = "" ;
this .elm.style.opacity = "" ;
// 獲取灰框
var ele = getGhostElement();
// 如果現(xiàn)在的鄰居不是原來的鄰居了
if (ele.nextSibling != this .origNextSibling) {
// 把被拖拽的這個節(jié)點(diǎn)插到灰框的前面
ele.parentNode.insertBefore( this .elm, ele.nextSibling);
// 標(biāo)明被拖拽了新的地方
returnValue = true ;
}
// 移除灰框,這是這個灰框的生命周期應(yīng)該就結(jié)束了
ele.parentNode.removeChild(ele);
// 修改完畢,顯示
Util.show();
if (Util.isOpera) {
// Opera的現(xiàn)實(shí)問題,要隱藏/顯示后才能刷新出變化
document.body.style.display = " none " ;
document.body.style.display = "" ;
}
return returnValue;
};
// 可拖拽Element的原形,用來將event綁定到各個鉤子,這部分市比較通用的,NETvibes也是基本完全相同的實(shí)現(xiàn)
// 這部分推薦看dindin的這個,也會幫助理解,http://www.jroller.com/page/dindin/?anchor=pro_Javascript_12
var Drag = {
// 對這個element的引用,一次只能拖拽一個Element
obj: null ,
// element是被拖拽的對象的引用,elementHeader就是鼠標(biāo)可以拖拽的區(qū)域
init: function (elementHeader, element) {
// 將start綁定到onmousedown事件,按下鼠標(biāo)觸發(fā)start
elementHeader.onmousedown = Drag.start;
// 將element存到header的obj里面,方便header拖拽的時候引用
elementHeader.obj = element;
// 初始化絕對的坐標(biāo),因為不是position=absolute所以不會起什么作用,但是防止后面onDrag的時候parse出錯了
if (isNaN(parseInt(element.style.left))) {
element.style.left = " 0px " ;
}
if (isNaN(parseInt(element.style.top))) {
element.style.top = " 0px " ;
}
// 掛上空Function,初始化這幾個成員,在Drag.init被調(diào)用后才幫定到實(shí)際的函數(shù),可以參照draggable里面的內(nèi)容
element.onDragStart = new Function();
element.onDragEnd = new Function();
element.onDrag = new Function();
},
// 開始拖拽的綁定,綁定到鼠標(biāo)的移動的event上
start: function (event) {
var element = Drag.obj = this .obj;
// 解決不同瀏覽器的event模型不同的問題
event = Drag.fixE(event);
// 看看是不是左鍵點(diǎn)擊
if (event.which != 1 ) {
// 除了左鍵都不起作用
return true ;
}
// 參照這個函數(shù)的解釋,掛上開始拖拽的鉤子
element.onDragStart();
// 記錄鼠標(biāo)坐標(biāo)
element.lastMouseX = event.clientX;
element.lastMouseY = event.clientY;
// 將Global的event綁定到被拖動的element上面來
document.onmouseup = Drag.end;
document.onmousemove = Drag.drag;
return false ;
},
// Element正在被拖動的函數(shù)
drag: function (event) {
// 解決不同瀏覽器的event模型不同的問題
event = Drag.fixE(event);
// 看看是不是左鍵點(diǎn)擊
if (event.which == 0 ) {
// 除了左鍵都不起作用
return Drag.end();
}
// 正在被拖動的Element
var element = Drag.obj;
// 鼠標(biāo)坐標(biāo)
var _clientX = event.clientY;
var _clientY = event.clientX;
// 如果鼠標(biāo)沒動就什么都不作
if (element.lastMouseX == _clientY && element.lastMouseY == _clientX) {
return false ;
}
// 剛才Element的坐標(biāo)
var _lastX = parseInt(element.style.top);
var _lastY = parseInt(element.style.left);
// 新的坐標(biāo)
var newX, newY;
// 計算新的坐標(biāo):原先的坐標(biāo)+鼠標(biāo)移動的值差
newX = _lastY + _clientY - element.lastMouseX;
newY = _lastX + _clientX - element.lastMouseY;
// 修改element的顯示坐標(biāo)
element.style.left = newX + " px " ;
element.style.top = newY + " px " ;
// 記錄element現(xiàn)在的坐標(biāo)供下一次移動使用
element.lastMouseX = _clientY;
element.lastMouseY = _clientX;
// 參照這個函數(shù)的解釋,掛接上Drag時的鉤子
element.onDrag(newX, newY);
return false ;
},
// Element正在被釋放的函數(shù),停止拖拽
end: function (event) {
// 解決不同瀏覽器的event模型不同的問題
event = Drag.fixE(event);
// 解除對Global的event的綁定
document.onmousemove = null ;
document.onmouseup = null ;
// 先記錄下onDragEnd的鉤子,好移除obj
var _onDragEndFuc = Drag.obj.onDragEnd();
// 拖拽完畢,obj清空
Drag.obj = null ;
return _onDragEndFuc;
},
// 解決不同瀏覽器的event模型不同的問題
fixE: function (ig_) {
if ( typeof ig_ == " undefined " ) {
ig_ = window.event;
}
if ( typeof ig_.layerX == " undefined " ) {
ig_.layerX = ig_.offsetX;
}
if ( typeof ig_.layerY == " undefined " ) {
ig_.layerY = ig_.offsetY;
}
if ( typeof ig_.which == " undefined " ) {
ig_.which = ig_.button;
}
return ig_;
}
};
// 下面是初始化的函數(shù)了,看看上面這些東西怎么被調(diào)用
var _IG_initDrag = function (el) {
// column那個容器,在google里面就是那個table布局的tbody,NETvibes用的<div>
Util.rootElement = el;
// 這個tbody的行
Util._rows = Util.rootElement.tBodies[ 0 ].rows[ 0 ];
// 列,google是3列,其實(shí)也可以更多
Util.column = Util._rows.cells;
// 用來存取可拖拽的對象
Util.dragArray = new Array();
var counter = 0 ;
for ( var i = 0 ; i < Util.column.length; i ++ ) {
// 搜索所有的column
var ele = Util.column[i];
for ( var j = 0 ; j < ele.childNodes.length; j ++ ) {
// 搜索每一column里面的所有element
var ele1 = ele.childNodes[j];
// 如果是div就把它初始化為一個draggable對象
if (ele1.tagName == " DIV " ) {
Util.dragArray[counter] = new draggable(ele1);
counter ++ ;
}
}
}
};
// google的頁面里可以拖動的部分的id是"t_1"
// 掛載到onload,載入完畢執(zhí)行。不過實(shí)際上google沒有用onload。
// 而是寫在頁面最下面,異曲同工吧,也許直接寫在頁面是種怪癖,或者也有可能是兼容性考慮。
// 請將下面兩條被注釋掉的代碼加,到你自己下載的一個google ig頁面里面,把里面的所有其余script刪除,掛上這個js也可以拖拽了,哈哈
// _table=document.getElementById("t_1");
// window.onload = _IG_initDrag(_table);
// 其實(shí)看懂這些代碼對學(xué)習(xí)Javascript很有益,希望對大家能有幫助
JavaScript技術(shù):對google個性主頁的拖拽效果的js的完整注釋[轉(zhuǎn)],轉(zhuǎn)載需保留來源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請第一時間聯(lián)系我們修改或刪除,多謝。