這篇文章詳細介紹了JS拖曳外掛程式的實作步驟,主要從以下六步驟做詳細分析,具體內容如下:
一、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; }
用自執行匿名函數將程式碼包起來,內部定義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、每個頁面只能有一個正在拖曳中的元素
為此,我們定義了唯一一個物件用來保存相關的配置:
这个对象中有三个属性:
(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表示有效拖拽区域:
然后修改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拖拽插件实现步骤的详细介绍,希望对大家有所帮助。