一个前端博客(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(); });
待续.....