快捷搜索:

javascript页面拖动效果的分析

你必然体会过igoogle,mybaidu的首页拖动效果,那么你可能感觉这是一个很高妙的问题,着实不然,这个拖放效果,也叫拖拽,学名Drag-and-drop ,是最常见的js殊效之一,本日我们就从道理到实现进行具体的阐发。

法度榜样道理

这里以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设置过后一样平常不要取消或改动,否则很轻易造成移位非常。

默认动作

对选择状态的文本内容、连接和图片等进行拖放操作会触发系统的默认动作,例如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),可以用下面的代码测试:

会发明背景点击触发不了事故,不过点击边框的话照样可以触发。

为什么呢?再用下面的代码测试:

应该能看出个大年夜概了,下面两个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,滚屏等这些还没斟酌到,等今后有必要了再来钻研拉。

您可能还会对下面的文章感兴趣: