, 需要的朋友可以参考下
实现 JS 拖拽插件主要从六个方面做介绍:一、js 拖拽插件的原理,二、根据原理实现的最基本效果,三、代码抽象与优化,四、扩展:有效的拖拽元素,五、性能优化和总结,六、jquery 插件化
Javascript 是一种由 Netscape 的 LiveScript 发展而来的原型化继承的基于对象的动态类型的区分大小写的客户端脚本语言,主要目的是为了解决服务器端语言,比如 Perl,遗留的速度问题,为客户提供更流畅的浏览效果。
一、js 拖拽插件的原理 二、根据原理实现的最基本效果 三、代码抽象与优化 四、扩展:有效的拖拽元素 五、性能优化和总结 六、jquery 插件化 js 拖拽是常见的网页效果,本文将从零开始实现一个简单的 js 插件。
一、js 拖拽插件的原理 常见的拖拽操作是什么样的呢?整过过程大概有下面几个步骤:
1、用鼠标点击被拖拽的元素
2、按住鼠标不放,移动鼠标
3、拖拽元素到一定位置,放开鼠标
这里的过程涉及到三个 dom 事件:onmousedown,onmousemove,onmouseup。所以拖拽的基本思路就是:
1、用鼠标点击被拖拽的元素触发 onmousedown
(1)设置当前元素的可拖拽为 true,表示可以拖拽
(2)记录当前鼠标的坐标 x,y
(3)记录当前元素的坐标 x,y
2、移动鼠标触发 onmousemove
(1)判断元素是否可拖拽,如果是则进入步骤 2,否则直接返回
(2)如果元素可拖拽,则设置元素的坐标
元素的 x 坐标 = 鼠标移动的横向距离 + 元素本来的 x 坐标 = 鼠标现在的 x 坐标 - 鼠标之前的 x 坐标 + 元素本来的 x 坐标
元素的 y 坐标 = 鼠标移动的横向距离 + 元素本来的 y 坐标 = 鼠标现在的 y 坐标 - 鼠标之前的 y 坐标 + 元素本来的 y 坐标
3、放开鼠标触发 onmouseup
(1)将鼠标的可拖拽状态设置成 false
回到顶部 二、根据原理实现的最基本效果 在实现基本的效果之前,有几点需要说明的:
1、元素想要被拖动,它的 postion 属性一定要是 relative 或 absolute
2、通过 event.clientX 和 event.clientY 获取鼠标的坐标
3、onmousemove 是绑定在 document 元素上而不是拖拽元素本身,这样能解决快速拖动造成的延迟或停止移动的问题
代码如下:
三、代码抽象与优化上面的代码要做成插件,要将其抽象出来,基本结构如下:
- var dragObj = document.getElementById("test");
- dragObj.style.left = "px";
- dragObj.style.top = "px";
- var mouseX, mouseY, objX, objY;
- var dragging = false;
- dragObj.onmousedown = function (event) {
- event = event || window.event;
- dragging = true;
- dragObj.style.position = "relative";
- mouseX = event.clientX;
- mouseY = event.clientY;
- objX = parseInt(dragObj.style.left);
- objY = parseInt(dragObj.style.top);
- }
- document.onmousemove = function (event) {
- event = event || window.event;
- if (dragging) {
- dragObj.style.left = parseInt(event.clientX - mouseX + objX) + "px";
- dragObj.style.top = parseInt(event.clientY - mouseY + objY) + "px";
- }
- }
- document.onmouseup = function () {
- dragging = false;
- }
- ; (function (window, undefined) {
- function Drag(ele) {}
- window.Drag = Drag;
- })(window, undefined);
用自执行匿名函数将代码包起来,内部定义 Drag 方法并暴露到全局中,直接调用 Drag,传入被拖拽的元素。
首先对一些常用的方法进行简单的封装:
- ; (function (window, undefined) {
- var dom = {
- //绑定事件
- on: function (node, eventName, handler) {
- if (node.addEventListener) {
- node.addEventListener(eventName, handler);
- }
- else {
- node.attachEvent("on" + eventName, handler);
- }
- },
- //获取元素的样式
- getStyle: function (node, styleName) {
- var realStyle = null;
- if (window.getComputedStyle) {
- realStyle = window.getComputedStyle(node, null)[styleName];
- }
- else if (node.currentStyle) {
- realStyle = node.currentStyle[styleName];
- }
- return realStyle;
- },
- //获取设置元素的样式
- setCss: function (node, css) {
- for (var key in css) {
- node.style[key] = css[key];
- }
- }
- };
- window.Drag = Drag;
- })(window, undefined);
在一个拖拽操作中,存在着两个对象:被拖拽的对象和鼠标对象,我们定义了下面的两个对象以及它们对应的操作:
首先的拖拽对象,它包含一个元素节点和拖拽之前的坐标 x 和 y:
- function DragElement(node) {
- this.node = node;//被拖拽的元素节点
- this.x = ;//拖拽之前的x坐标
- this.y = ;//拖拽之前的y坐标
- }
- DragElement.prototype = {
- constructor: DragElement,
- init: function () {
- this.setEleCss({
- "left": dom.getStyle(node, "left"),
- "top": dom.getStyle(node, "top")
- })
- .setXY(node.style.left, node.style.top);
- },
- //设置当前的坐标
- setXY: function (x, y) {
- this.x = parseInt(x) || ;
- this.y = parseInt(y) || ;
- return this;
- },
- //设置元素节点的样式
- setEleCss: function (css) {
- dom.setCss(this.node, css);
- return this;
- }
- }
还有一个对象是鼠标,它主要包含 x 坐标和 y 坐标:
- function Mouse() {
- this.x = ;
- this.y = ;
- }
- Mouse.prototype.setXY = function (x, y) {
- this.x = parseInt(x);
- this.y = parseInt(y);
- }
这是在拖拽操作中定义的两个对象。
如果一个页面可以有多个拖拽元素,那应该注意什么:
1、每个元素对应一个拖拽对象实例
2、每个页面只能有一个正在拖拽中的元素
为此,我们定义了唯一一个对象用来保存相关的配置:
- var draggableConfig = {
- zIndex: ,
- draggingObj: null,
- mouse: new Mouse()
- };
这个对象中有三个属性:
(1)zIndex:用来赋值给拖拽对象的 zIndex 属性,有多个拖拽对象时,当两个拖拽对象重叠时,会造成当前拖拽对象有可能被挡住,通过设置 zIndex 使其显示在最顶层
(2)draggingObj:用来保存正在拖拽的对象,在这里去掉了前面的用来判断是否可拖拽的变量,通过 draggingObj 来判断当前是否可以拖拽以及获取相应的拖拽对象
(3)mouse:唯一的鼠标对象,用来保存当前鼠标的坐标等信息
最后是绑定 onmousedown,onmouseover,onmouseout 事件,整合上面的代码如下:
- ; (function (window, undefined) {
- var dom = {
- //绑定事件
- on: function (node, eventName, handler) {
- if (node.addEventListener) {
- node.addEventListener(eventName, handler);
- }
- else {
- node.attachEvent("on" + eventName, handler);
- }
- },
- //获取元素的样式
- getStyle: function (node, styleName) {
- var realStyle = null;
- if (window.getComputedStyle) {
- realStyle = window.getComputedStyle(node, null)[styleName];
- }
- else if (node.currentStyle) {
- realStyle = node.currentStyle[styleName];
- }
- return realStyle;
- },
- //获取设置元素的样式
- setCss: function (node, css) {
- for (var key in css) {
- node.style[key] = css[key];
- }
- }
- };
- //#region 拖拽元素类
- function DragElement(node) {
- this.node = node;
- this.x = ;
- this.y = ;
- }
- DragElement.prototype = {
- constructor: DragElement,
- init: function () {
- this.setEleCss({
- "left": dom.getStyle(node, "left"),
- "top": dom.getStyle(node, "top")
- })
- .setXY(node.style.left, node.style.top);
- },
- setXY: function (x, y) {
- this.x = parseInt(x) || ;
- this.y = parseInt(y) || ;
- return this;
- },
- setEleCss: function (css) {
- dom.setCss(this.node, css);
- return this;
- }
- }
- //#endregion
- //#region 鼠标元素
- function Mouse() {
- this.x = ;
- this.y = ;
- }
- Mouse.prototype.setXY = function (x, y) {
- this.x = parseInt(x);
- this.y = parseInt(y);
- }
- //#endregion
- //拖拽配置
- var draggableConfig = {
- zIndex: ,
- draggingObj: null,
- mouse: new Mouse()
- };
- function Drag(ele) {
- this.ele = ele;
- function mouseDown(event) {
- var ele = event.target || event.srcElement;
- draggableConfig.mouse.setXY(event.clientX, event.clientY);
- draggableConfig.draggingObj = new DragElement(ele);
- draggableConfig.draggingObj
- .setXY(ele.style.left, ele.style.top)
- .setEleCss({
- "zIndex": draggableConfig.zIndex++,
- "position": "relative"
- });
- }
- ele.onselectstart = function () {
- //防止拖拽对象内的文字被选中
- return false;
- }
- dom.on(ele, "mousedown", mouseDown);
- }
- dom.on(document, "mousemove", function (event) {
- if (draggableConfig.draggingObj) {
- var mouse = draggableConfig.mouse,
- draggingObj = draggableConfig.draggingObj;
- draggingObj.setEleCss({
- "left": parseInt(event.clientX - mouse.x + draggingObj.x) + "px",
- "top": parseInt(event.clientY - mouse.y + draggingObj.y) + "px"
- });
- }
- })
- dom.on(document, "mouseup", function (event) {
- draggableConfig.draggingObj = null;
- })
- window.Drag = Drag;
- })(window, undefined);
调用方法:Drag(document.getElementById("obj"));
注意的一点,为了防止选中拖拽元素中的文字,通过 onselectstart 事件处理程序 return false 来处理这个问题。
四、扩展:有效的拖拽元素 我们常见的一些拖拽效果很有可能是这样的:
弹框的顶部是可以进行拖拽操作的,内容区域是不可拖拽的,怎么实现这样的效果呢:
首先优化拖拽元素对象如下,增加一个目标元素 target,表示被拖拽对象,在上图的登录框中,就是整个登录窗口。
被记录和设置坐标的拖拽元素就是这个目标元素,但是它并不是整个部分都是拖拽的有效部分。我们在 html 结构中为拖拽的有效区域添加类 draggable 表示有效拖拽区域:
- <div id="obj" class="dialog" style="position:relative;left:px">
- <div class="header draggable">
- 拖拽的有效元素
- </div>
- <div class="content">
- 拖拽对象
- </div>
- </div>
然后修改 Drag 方法如下:
- function drag(ele) {
- var dragNode = (ele.querySelector(".draggable") || ele);
- dom.on(dragNode, "mousedown", function (event) {
- var dragElement = draggableConfig.dragElement = new DragElement(ele);
- draggableConfig.mouse.setXY(event.clientX, event.clientY);
- draggableConfig.dragElement
- .setXY(dragElement.target.style.left, dragElement.target.style.top)
- .setTargetCss({
- "zIndex": draggableConfig.zIndex++,
- "position": "relative"
- });
- }).on(dragNode, "mouseover", function () {
- dom.setCss(this, draggableStyle.dragging);
- }).on(dragNode, "mouseout", function () {
- dom.setCss(this, draggableStyle.defaults);
- });
- }
主要修改的是绑定 mousedown 的节点变成了包含 draggable 类的有效元素,如果不含有 draggable,则整个元素都是有效元素。
五、性能优化和总结 由于 onmousemove 在一直调用,会造成一些性能问题,我们可以通过 setTimout 来延迟绑定 onmousemove 事件,改进 move 函数如下
- function move(event) {
- if (draggableConfig.dragElement) {
- var mouse = draggableConfig.mouse,
- dragElement = draggableConfig.dragElement;
- dragElement.setTargetCss({
- "left": parseInt(event.clientX - mouse.x + dragElement.x) + "px",
- "top": parseInt(event.clientY - mouse.y + dragElement.y) + "px"
- });
- dom.off(document, "mousemove", move);
- setTimeout(function () {
- dom.on(document, "mousemove", move);
- }, );
- }
- }
总结:
整个拖拽插件的实现其实很简单,主要是要注意几点
1、实现思路:元素拖拽位置的改变就等于鼠标改变的距离,关键在于获取鼠标的变动和元素原本的坐标
2、通过 setTimeout 来延迟加载 onmousemove 事件来提供性能
六、jquery 插件化 简单地将其封装成 jquery 插件,主要是相关的 dom 方法替换成 jquery 方法来操作
- ; (function ($, window, undefined) {
- //#region 拖拽元素类
- function DragElement(node) {
- this.target = node;
- node.onselectstart = function () {
- //防止拖拽对象内的文字被选中
- return false;
- }
- }
- DragElement.prototype = {
- constructor: DragElement,
- setXY: function (x, y) {
- this.x = parseInt(x) || ;
- this.y = parseInt(y) || ;
- return this;
- },
- setTargetCss: function (css) {
- $(this.target).css(css);
- return this;
- }
- }
- //#endregion
- //#region 鼠标元素
- function Mouse() {
- this.x = ;
- this.y = ;
- }
- Mouse.prototype.setXY = function (x, y) {
- this.x = parseInt(x);
- this.y = parseInt(y);
- }
- //#endregion
- //拖拽配置
- var draggableConfig = {
- zIndex: ,
- dragElement: null,
- mouse: new Mouse()
- };
- var draggableStyle = {
- dragging: {
- cursor: "move"
- },
- defaults: {
- cursor: "default"
- }
- }
- var $document = $(document);
- function drag($ele) {
- var $dragNode = $ele.find(".draggable");
- $dragNode = $dragNode.length > ? $dragNode : $ele;
- $dragNode.on({
- "mousedown": function (event) {
- var dragElement = draggableConfig.dragElement = new DragElement($ele.get());
- draggableConfig.mouse.setXY(event.clientX, event.clientY);
- draggableConfig.dragElement
- .setXY(dragElement.target.style.left, dragElement.target.style.top)
- .setTargetCss({
- "zIndex": draggableConfig.zIndex++,
- "position": "relative"
- });
- },
- "mouseover": function () {
- $(this).css(draggableStyle.dragging);
- },
- "mouseout": function () {
- $(this).css(draggableStyle.defaults);
- }
- })
- }
- function move(event) {
- if (draggableConfig.dragElement) {
- var mouse = draggableConfig.mouse,
- dragElement = draggableConfig.dragElement;
- dragElement.setTargetCss({
- "left": parseInt(event.clientX - mouse.x + dragElement.x) + "px",
- "top": parseInt(event.clientY - mouse.y + dragElement.y) + "px"
- });
- $document.off("mousemove", move);
- setTimeout(function () {
- $document.on("mousemove", move);
- }, );
- }
- }
- $document.on({
- "mousemove": move,
- "mouseup": function () {
- draggableConfig.dragElement = null;
- }
- });
- $.fn.drag = function (options) {
- drag(this);
- }
- })(jQuery, window, undefined)
以上就是本文对 JS 拖拽插件实现步骤的详细介绍,希望对大家有所帮助。
来源: http://www.phperz.com/article/17/0626/270177.html