LeavingMe.net

JavaScript 实现模拟拖放

2011-02-14 22:24:00

拖放(Drag-and-Drop)是网页中经常出现的效果,比如:iGoogle里的gadget、QZone里的模块、cnblogs里的插入代码对话框等。咋一看对于实现该效果毫无头绪,实际上理清思路就比较好实现了。 问:把大象放冰箱里分几步?
答:分三步。1. 打开冰箱门;2. 把大象放进去;3. 关上冰箱门。 那拖放效果分几步呢?
1. 按下鼠标mousedown;2. 移动鼠标mousemove;3. 松开鼠标mouseup。 **一、简单实现**
根据上面的思路开始编码。 HTML 代码:#box是拖放的对象
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>drag & drop</title>
</head>
<body>
<div id="box">BOX</div>
<div id="debug"></div>
</body>
</html>

CSS 代码:#box 绝对定位
<style type="text/css">
body {
    margin: 0;
    padding: 0;
}

box {

position: absolute;
cursor: default;
width: 100px;
height: 100px;
border: 1px solid red;

} </style>

JavaScript 代码:为 box 对象绑定了 4 个事件。在鼠标按下时设置全局标志 mousedown = true,鼠标移动时判断全局标志 mousedown,为 true 就设置 box 对象的样式,松开鼠标时设置全部标志 mousedown = false
<script type="text/javascript">
window.onload = function() {
    var box = document.getElementById("box");
    var debug = document.getElementById("debug");

box.addEventListener('selectstart', function(event) {
    event.returnValue = false;
});

box.addEventListener('mousedown', function(event) {
    mousedown = true;
});

box.addEventListener('mouseup', function() {
    mousedown = false;
});

box.addEventListener('mousemove', function(event) {
    if(mousedown) {
        //改变 box 对象的样式
    }
});

}; </script>

JavaScript 代码:移动鼠标时改变box对象的样式
box.addEventListener('mousemove', function(event) {
    if(mousedown) {
        box.style.left = (event.clientX - 50) + 'px';
        box.style.top = (event.clientY - 50) + 'px';
    }
});
拖放效果基本实现了,点击查看Demo(请使用 Chrome、Safari 或 Firefox)
通过运行 Demo 会发现存在着一些问题:
1. 当鼠标移动过快,BOX会被甩掉;
2. 当鼠标移动到浏览器外部,再移动到内部时,松开鼠标,BOX会继续移动;
3. 移动时鼠标相对BOX的位置不是鼠标第一次点击时相对BOX的位置。

二、后续完善

针对上面的问题,改进方案如下:
JavaScript代码:当按下鼠标时获取鼠标指针和 box 对象的间距,移动鼠标修改 box 对象样式时减去该间距。同时移动鼠标事件和松开鼠标事件的绑定对象修改为document
<script type="text/javascript">
window.onload = function() {
    var box = document.getElementById("box");
    var debug = document.getElementById("debug");

var mousedown = false;
var offsetX = 0, offsetY = 0;

box.addEventListener('selectstart', function(event) {
    event.returnValue = false;
});

box.addEventListener('mousedown', function(event) {
    mousedown = true;
    offsetX = event.layerX;
    offsetY = event.layerY;
});

document.addEventListener('mouseup', function() {
    mousedown = false;
});

document.addEventListener('mousemove', function(event) {
    if(mousedown) {
        //改变 box 对象的样式
        box.style.left = (event.clientX - offsetX) + 'px';
        box.style.top = (event.clientY - offsetY) + 'px';
    }
});

}; </script>

点击查看Demo(请使用 Chrome、Safari 或 Firefox)

三、jQuery 插件

为了方便使用该功能包装成jQuery的插件。
jQuery 插件:
(function($) {
    var data = {
        currentObj : null,
        offsetX : 0,
        offsetY : 0,
        flag : false
    };

$(document).mouseup(function() {
    data.current = null;
    data.flag = false;
});

$(document).mousemove(function(event) {
    if(data.flag &amp;&amp; data.currentObj) {
        data.currentObj.css({
            left : (event.clientX - data.offsetX) + 'px',
            top : (event.clientY - data.offsetY) + 'px'
        });
    }
});

$.fn.extend({
    draggable : function() {
        return this.each(function() {
            var $this = $(this);
            $this.bind('selectstart', function() {
                return false;
            }).mousedown(function(event) {
                data.currentObj = $this;
                data.offsetX = event.layerX;
                data.offsetY = event.layerY;
                data.flag = true;
            });
        });
    }
});

})(jQuery);

使用方法:
$(function() {
    $('#box, #otherBox').draggable();
});
点击查看Demo(请使用 Chrome、Safari 或 Firefox)

四、未来跟进

1. IE兼容性问题
2. 多个元素拖放时z-index的问题

五、参考资料

JavaScript实现最简单的拖拽效果:http://www.zhangxinxu.com/wordpress/?p=683
jQuery 插件创作:http://docs.jquery.com/Plugins/Authoring

comments powered by Disqus