이 글에서는 JS 드래그 앤 드롭 플러그인의 구현 단계를 구체적으로 소개하며, 구체적인 내용은 다음과 같습니다.
1. js 드래그 앤 드롭 플러그인의 원리
2. 원리를 바탕으로 구현되는 가장 기본적인 효과
3. 코드 추상화 및 최적화
4. 확장: 효과적인 드래그 앤 드롭 요소
5. 성능 최적화 및 요약
6. jquery 플러그인
JS 드래그 앤 드롭은 일반적인 웹 페이지 효과입니다. 이 기사에서는 간단한 JS 플러그인을 처음부터 구현합니다.
1. js 드래그 앤 드롭 플러그인의 원리
일반적인 드래그 앤 드롭 작업이란 무엇입니까? 전체 프로세스는 다음 단계로 구성됩니다.
1. 드래그한 요소를 마우스로 클릭
2. 마우스를 누른 상태에서 마우스를 움직입니다
3. 요소를 특정 위치로 드래그하고 마우스 버튼을 놓습니다.
여기서 프로세스에는 onmousedown, onmousemove, onmouseup의 세 가지 DOM 이벤트가 포함됩니다. 따라서 드래그의 기본 아이디어는 다음과 같습니다.
1. 마우스로 드래그한 요소를 클릭하면 onmousedown이 실행됩니다.
(1) 현재 요소의 드래그 가능성을 true로 설정하여 드래그할 수 있음을 나타냅니다.
(2) 현재 마우스 좌표 x, y를 기록
(3) 현재 요소의 x,y 좌표를 기록합니다
2. 마우스를 움직여 마우스 이동을 실행하세요
(1) 요소를 드래그 앤 드롭할 수 있는지 확인하고, 그렇다면 2단계로 이동하고, 그렇지 않으면
으로 바로 돌아갑니다.(2) 요소를 드래그할 수 있는 경우 요소의 좌표를 설정합니다
요소의 x 좌표 = 마우스로 이동한 수평 거리 요소의 원래 x 좌표 = 마우스의 현재 x 좌표 - 마우스 이전의 x 좌표.
요소의요소의 y 좌표 = 마우스로 이동한 수평 거리 요소의 원래 y 좌표 = 마우스의 현재 y 좌표 - 마우스 이전의 y 좌표
3. 마우스를 떼면 onmouseup이 실행됩니다.
(1) 마우스 드래그 가능 상태를 false로 설정
맨 위로
2. 원리에 따른 가장 기본적인 효과
기본 효과를 구현하기 전에 먼저 설명해야 할 몇 가지 사항이 있습니다.
1. 요소를 드래그하려면 위치 속성이 상대적이거나 절대적이어야 합니다
2. event.clientX 및 event.clientY를 통해 마우스 좌표를 가져옵니다
3. onmousemove는 드래그 요소 자체가 아닌 문서 요소에 바인딩되어 빠른 드래그로 인해 발생하는 지연 또는 정지 문제를 해결할 수 있습니다
코드는 다음과 같습니다.
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) { 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拖拽插件实现步骤的详细介绍,希望对大家有所帮助。