拖动效果_拖动原理_setCapture鼠标捕获的使用,以及setPointerCapture的介绍
2017-01-08 01:15

 在实际应用中,我们有时需要让一个元素能够根据用户的拖动来改变位置,最常见的就是一些弹窗,弹窗具有拖动的功能,就可以让用户通过拖动改变弹窗的位置,确实是一个很炫的功能,而且有时很实用,可以通过拖动弹窗,在不关闭弹窗的情况下查看弹窗遮挡下的元素。还有一些场景我们需要页面布局对用户可以自行DIY操作,就需要让元素可以拖动。

  下面我们来将一下拖动的实现:

我们先定义一个div元素,用于拖动效果:

<style>
 .movetg{
    width:300px;
    height:300px;
    border:1px solid #ccc;
    background:darkorange;
    cursor:default;
    position:absolute;
    z-Index:2;
    left:100px;
    top:50px;
    box-shadow:2px 4px 12px rgba(0,0,0,0.6);
 }
</style>

<div class="movetg" id="tt">

</div>

drag.png

div的模样长成上面这样,截图来自chrome浏览器

注意,div是absolute定位的,这样可以通过left和top来改变这个div的位置。

通过js实现拖动的过程分三步:

 @1:

  在div元素上定义ommousedown事件,当按下鼠标左键的时候,发生onmousedown时间,这时候我们记录鼠标的开始结束位置,并且记录div元素的位置,设置鼠标按下状态为true,代码如下:

var dom=document.getElementById("tt");

var sx,sy,ex,ey,cx,xy,isdown=false;
var rect=dom.getBoundingClientRect();
var wd=rect.right-rect.left;
var ht=rect.bottom-rect.top;
dom.onmousedown=function(event){
    var evt=window.event||event;
    sx=ex=evt.clientX;
    sy=ey=evt.clientY;
    cx=parseInt(curCss(this,"left"));
    cy=parseInt(curCss(this,"top"));
    isdown=true;
};

其中得到div位置的方法:curCss是获取div对象的最终属性的方法,前面文章将getComputedStyle和currentStyle的时候提过。

你可以点击查看通过getComputedStyle和currentStyle获取元素样式的功能

@2:

  在document对象上定义mousemove事件,在事件回调用获取event对象当前的位置,并定义updatePos改变div位置的方法更新div的位置,实现拖动效果。

  代码如下:

document.onmousemove=function(event){
    if(!isdown){
        return;
    }
    var evt=window.event||event;
    ex=evt.clientX;
    ey=evt.clientY;
    updateDivPos();
};
function updateDivPos(){
  var rstleft=cx+(ex-sx);
  var rsttop=cy+(ey-sy);
    if(rstleft+wd>document.documentElement.clientWidth){
        rstleft=document.documentElement.clientWidth-wd;
    }
    if(rsttop+ht>document.documentElement.clientHeight){
        rsttop=document.documentElement.clientHeight-ht;
    }
    dom.style.left=rstleft+"px";
    dom.style.top=rsttop+"px";
}

其中的updateDivPos方法改变div的位置

left的位置:cx+(ex-sx)计算公式是cx表示div的原始left,ex-sx表示鼠标clientX的位置的变化差值。top位置算法相同。

if判断是为了让div的位置不能超过浏览器的宽度和高度,把div限制在client客户区视区的范围内。

这时候我们已经完成了拖动功能的大部分代码,已经能看到拖动的效果,还差最后一个mouseup事件。

@3:

  通过mouseup事件改变isdown标识符为false,避免在鼠标没有按下时也会触发mousemove事件的回调作用。

dom.onmouseup=function(event){
    if(isdown){
        isdown=false;
        
    }
}

mouseup事件的功能很简单,就是重置isdown标识符。

在时候我们实现的拖动功能以及很OK了。

captrue.png

如果我们把浏览器缩小,在div上按下鼠标左键,将鼠标移动到浏览器外面移动,div还是可以跟随鼠标移动的,但是在IE下如果在浏览器外面释放鼠标,则第二次移动div时鼠标会呈现文本拖动样式,拖动效果不能实现,要解决这个问题,我们需要使用IE的特色功能。

setCapture,relaseCapture,这一对方法就是用来设置元素对鼠标事件的捕捉和释放的。

如果对div调用了setCapture方法,那么在div外面已经浏览器窗口外发生的鼠标事件都会通知div上绑定的事件回调事件进行执行。

所以我们需要对上述事件稍加修改,加入setCapture方法来解决IE下的bug。

dom.onmousedown=function(event){
    var evt=window.event||event;
    sx=ex=evt.clientX;
    sy=ey=evt.clientY;
    cx=parseInt(curCss(this,"left"));
    cy=parseInt(curCss(this,"top"));
   if(this.setCapture){
        this.setCapture();
    }
    isdown=true;
};
document.onmouseup=function(event){
    if(isdown){
        isdown=false;
        if(dom.releaseCapture) {
           dom.releaseCapture();
        }
    }
}

因为其他浏览器大部分不支持setCapture方法,所以要先做能力判断然后调用(测试目前最新Firefox方法已经偷偷摸摸的添加了对setCapture方法的支持)。

可能有一些人要问setCapture方法能捕获鼠标事件,这么强大的功能别的浏览器有没有兼容的实现方法,答案是否定的,不过Microsoft提出的pointerEvents事件架构中有setPointerCapture,releasePointerCapture两个方法来设置对象对鼠标事件的捕获,这个事件架构是为了能够统一web的事件机制,其中涵盖了鼠标,触摸,笔触等的事件。IE10以上支持,经测试目前最新的55版本的chrome浏览器已经支持此事件架构。但是火狐不支持,此事件架构的浏览器支持程度还不是太完善,需要等等才能用。

下面我们给一个通过我们上述讲述的拖动原理实现的拖动效果的demo,点击查看拖动效果demo

最后在说一下mousemove,mouseup事件绑定在document对象上不知是为了能让chrome,firefox浏览器能对浏览器外的鼠标事件进行捕获,更重要的是,如果拖动元素很小的话,我们鼠标拖动速度快,就会出现鼠标的移动快过div的移动,在各个浏览器下的就会出现拖不动的现象。

dragsmall.png

就如同上面的截图,如果拖动元素是这么小,就会出现卡顿的现象,当然这IE下,或者最新Firefox偷偷实现了setCapture功能下,绑定事件到div元素上是能实现连贯的操作的,因为他们捕获了鼠标事件。

在chrome下快速拖动查看拖不动的现象。这个demo,你可以点击体验一下,中IE和最新版的firefox是可以连续拖动的,在chrome等浏览器下,缓慢拖动能拖得动,但快速拖动会出现拖不动的事件,这个原因是@1,没有捕获事件,@2,鼠标移动速度快,div没有跟上,之后鼠标的mousemove事件是发生在document对象,而不是div上了,所以div就不能继续跟随鼠标的移动发生拖动效果。

总结:

   本文我们实现了拖动效果,并清晰的讲述了拖动的原理和拖动应该注意的地方。因为最新版chrome支持PointerEvents事件架构,所以事件绑定在div上,我们可以用setPointerCapture(evt.pointerId)来让div捕获鼠标事件。但火狐目前不支持,并且火狐实现的setCapture将来是否会去掉也不能保障,所以我们最佳选择就是将鼠标的mousemove和mouseup事件绑定在document对象上。


原创文章,转载请注明来自:妹纸前端-www.webfront-js.com.
阅读(6000)
辛苦了,打赏喝个咖啡
微信
支付宝
妹纸前端
妹纸前端工作室 | 文章不断更新中
京ICP备16005385号-1