一个前端博客(6)——拖拽效果

从简单开始,拖拽效果的思路就是利用三个事件:onmousedown, onmousemove, onmouseup。

我们首先来创建drag()方法的基本结构:

Tar.prototype.dray = function() {
    for(var i = 0; i < this.elements.length; i++) {
        this.elements[i].onmousedown = function() {
            document.onmousemove = function() {
                
            };
            document.onmouseup = function() {
                
            };
        };
    }
    return this;
}

首先鼠标开始点击的那个元素发生了onmousedown事件,然后在此事件上我们又发了onmousemove事件,最后松开鼠标,发生onmouseup事件。

onmousedown事件是在我们所选择的元素上发生的,而鼠标的onmousemove是在整个文档发生的,最后鼠标松开也是如此。

然后我们开始做些比较复杂的事。实现拖拽效果除了要用到这些事件以外,我们还要利用事件对象Event,鼠标的坐标(event.clientY和event.clientX),元素偏移量(offsetLeft,offsetTop).

在这儿之前我们先在tool.js里添加一个工具函数getEvent(),获取Event对象

function getEvent(event) {
    return event || window.event;
}

这样我们在后面用到事件对象,就可以用它来获取。

下面我们围绕这个图来说明拖拽效果的计算原理。

当你点击里面的框div的时候(之后我们都叫成div),我们可以获得鼠标的坐标clientX和div左偏移量offsetLeft,我们可以得到diffX的值(e.clientX - div.offsetLeft)。当开始拖动的时候,clientX变化,offsetLeft变化,diffX没有变化。但是注意,这里的clientX的变化和offsetLeft变化是不同的,clientX变化是随鼠标坐标改变而改变,但是offsetLeft是当计算后才得到的。

换种说法就是:div想要随鼠标移动,必须要改变它的left值,而left=clientX - diffX.当left改变后,它的offsetLeft才改变。div的top值也是这样得到的:

Tar.prototype.dray = function() {
    for(var i = 0; i < this.elements.length; i++) {
        this.elements[i].onmousedown = function(e) {
            var e = getEvent(e);
            var _this = this;
            var diffX = e.clientX - _this.offsetLeft;
            var diffY = e.clientY - _this.offsetTop;
            document.onmousemove = function(e) {
                var e = getEvent(e);
                var left = e.clientX - diffX;
                var top = e.clientY - diffY;
                _this.style.left = left + "px";
                _this.style.top = top + "px";
            };
            document.onmouseup = function() {
                
            };
        };
    }
    return this;
}

最后当我们松开鼠标的时候,我们需要清除掉onmousemove和onmouseup事件绑定。很简单,赋值为null就好.

            document.onmouseup = function() {
                this.onmousemove = null;
                this.onmouseup = null;
            };

这样我们基本就完成了拖拽。

考虑细节

首先第一个问题就是对于低版本的Firefox,当div里什么都没有,使用拖拽的话,会出现问题,这是由于它的默认行为。所以要阻止它的默认行为。

首先我又要在tool.js添加一个工具函数:

function preDef(event) {
    var e = getEvent(event);
    if(typeof e.preventDefault != 'undefined') {//W3C
        e.preventDefault();
    }else{//IE
        e.returnValue = false;
    }
}

我们在点下鼠标的时候,阻止他的默认行为就可以了:

Tar.prototype.drag = function() {
    for(var i = 0; i < this.elements.length; i++) {
        this.elements[i].onmousedown = function(e) {
            var e = getEvent(e);
            preDef(e);
                      .............
        };
    }
    return this;
}

下一个问题就是,当我们拖拽div到最左边的是,会过头,而不是不允许。

这里其实就是要限制left的范围,0<left<浏览器宽度 - div的宽度,当left<0要使left为0,当left>浏览器宽度 - div的宽度的时候,要使left为浏览器宽度 - div的宽度。高度也是类似。这里用到了之前的getInner()对象和offsetWidth,offsetHeight.

Tar.prototype.dray = function() {
    for(var i = 0; i < this.elements.length; i++) {
        this.elements[i].onmousedown = function(e) {
            var e = getEvent(e);
            preDef(e);
            var _this = this;
            var diffX = e.clientX - _this.offsetLeft;
            var diffY = e.clientY - _this.offsetTop;
            document.onmousemove = function(e) {
                var e = getEvent(e);
                var left = e.clientX - diffX;
                var top = e.clientY - diffY;
                if(left < 0) {
                    left = 0;
                }else if(left > getInner().width - _this.offsetWidth) {
                    left = getInner.width - _this.offsetWidth;
                }
                if(top < 0) {
                    top = 0;
                }else if(top > getInner().height - _this.offsetHeight) {
                    top = getInner().height - _this.offsetHeight;
                }
                _this.style.left = left + "px";
                _this.style.top = top + "px";
            };
            document.onmouseup = function() {
                this.onmousemove = null;
                this.onmouseup = null;
            };
        };
    }
    return this;
}

下一个问题就是IE独有的一个问题,IE浏览器在拖出浏览器外部的时候,会出现空白。解决方法也是IE浏览器独有的方法:

if(typeof _this.setCapture != 'undefined') {
                _this.setCapture();
            }
 if(typeof _this.releaseCapture != 'undefined') {
                    _this.releaseCapture();
                }

完整代码如下:

//拖拽效果
Tar.prototype.drag = function() {
    for(var i = 0; i < this.elements.length; i++) {
        this.elements[i].onmousedown = function(e) {
            var e = getEvent(e);
            preDef(e);
            var _this = this;
            var diffX = e.clientX - _this.offsetLeft;
            var diffY = e.clientY - _this.offsetTop;
            if(typeof _this.setCapture != 'undefined') {
                _this.setCapture();
            }
            document.onmousemove = function(e) {
                var e = getEvent(e);
                var left = e.clientX - diffX;
                var top = e.clientY - diffY;
                if(left < 0) {
                    left = 0;
                }else if(left > getInner().width - _this.offsetWidth) {
                    left = getInner().width - _this.offsetWidth;
                }
                if(top < 0) {
                    top = 0;
                }else if(top > getInner().height - _this.offsetHeight) {
                    top = getInner().height - _this.offsetHeight;
                }

                _this.style.left = left + "px";
                _this.style.top = top + "px";
            }
            document.onmouseup = function() {
                this.onmousemove = null;
                this.onmouseup = null;
                if(typeof _this.releaseCapture != 'undefined') {
                    _this.releaseCapture();
                }
            }
        };
    }
    return this;
}

最后一个问题就是,当我们改变浏览器大小的时候,即使我们拖拽了div,可是由于触发了onresize事件,所以它会居中。所以我们要修改我们的app.js代码:

    //登录框
    var login = $().getId("login");
    var screen = $().getId("screen");
    login.center(350,250);
    login.resize(function() {
        //login.center(350,250);//修改处
        if(login.css("display") == "block") {
            screen.lock();
        }
    });
    $().getClass("close").click(function() {
        login.css("display","none");
        screen.unlock();
    });
    $().getClass("login").click(function() {
        login.css("display","block");
        screen.lock();
    });
    login.drag();

我们把login.center(350,250);去掉。这样就可以了。不过当缩放的时候,会出现看不到div的情况(缩放过头)。这个时候,我们需要修改我们的resize()方法.除了要执行fn函数以外,我们还要对当前元素的定位有个限制,当前元素的偏移量要小于浏览器的大小 - 元素的大小。如果大于了则就将浏览器的大小 - 元素的大小赋值给它。代码如下:

//窗口大小变化事件
Tar.prototype.resize = function(fn) {
    for(var i = 0; i < this.elements.length; i++) {
        var element = this.elements[i];
        window.onresize = function() {
            fn();
            if(element.offsetLeft > getInner().width - element.offsetWidth) {
                element.style.left = getInner().width - element.offsetWidth + 'px';
            }
            if(element.offsetTop > getInner().height - element.offsetHeight) {
                element.style.top = getInner().height - element.offsetHeight + 'px';
            }
        };
    }
    return this;
}

最后我们为了更好的用户体验,我们在点击登录的时候希望能看到登录框居中,所以修改点击登录的代码:

   $().getClass("login").click(function() {
        login.center(350,250);//修改的地方
        login.css("display","block");
        screen.lock();
    });

待续.....

posted @ 2015-10-28 21:52  targeral  阅读(247)  评论(0编辑  收藏  举报