从简单开始,拖拽效果的思路就是利用三个事件: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();
});
待续.....