在线一键建站系统,网站建设简图,建设网站准备资料,免费源码资源源码站入口因业务场景需要一个可拖拽修改节点位置的树形组件#xff0c;因此动手撸了一个#xff0c;乘此机会摸了一把html5原生拖拽。近期有时间将核心部分代码抽出#xff0c;简单说下实现方式。1.树形结构-组件递归使用树形结构非常简单#xff0c;tree组件作为父组件#xff0c;…因业务场景需要一个可拖拽修改节点位置的树形组件因此动手撸了一个乘此机会摸了一把html5原生拖拽。近期有时间将核心部分代码抽出简单说下实现方式。1.树形结构-组件递归使用树形结构非常简单tree组件作为父组件结构如下tree.vue复制代码vue组件允许在它们自己的模板中调用自身因此可以形成树形结构在组件中必须填写唯一的name。tree-node.vue复制代码2.HTML5拖拽api1.draggable属性规定元素是否可拖动,目前Internet Explorer 9, Firefox, Opera, Chrome, and Safari 支持 draggable 属性2.HTML 5 拖放apiondragstart 元素开始被拖动时触发 作用在拖拽元素上ondragenter当拖曳元素进入目标元素的时候触发的事件作用在目标元素上ondragover拖拽元素在目标元素上移动的时候触发的事件作用在目标元素上ondragleave拖拽元素拖离开了目标元素时触发作用在目标元素上ondrop被拖拽的元素在目标元素上同时鼠标放开触发的事件作用在目标元素上ondragend当拖拽完成后触发的事件作用在被拖曳元素上3.拖拽节点定义变量处理拖拽节点需要几个关键变量当前拖拽的节点拖拽时经过的节点最终放置的节点因此定义了一个用于保存拖拽信息的对象dragOverStatus: {overNodeKey: ,dropPosition: ,dragNode: {}}复制代码绑定拖拽事件这里将ondragstart事件绑定在子元素上将其他事件绑定在父元素上因为在测试真机IE10的时候发现ondragstart和其他事件绑定在同一个元素上无法触发ondragenter等事件。复制代码mounted() {//绑定拖拽事件if (this.root.draggable) {this.$refs.draggAbleDom.draggable !this.nodeData.noDrag;this.$refs.draggAbleDom.ondragstart this.onDragStart;this.$refs.dropTarget.ondragenter this.onDragEnter;this.$refs.dropTarget.ondragover this.onDragOver;this.$refs.dropTarget.ondragleave this.onDragLeave;this.$refs.dropTarget.ondrop this.onDrop;this.$refs.dropTarget.ondragend this.onDragEnd;}}复制代码触发某节点的拖拽事件时就可以从拖拽事件里拿到当前节点实例。使用HTML5提供的专门的拖拽与拖放API原生的实现了复杂的操作不需要自己用鼠标事件模拟因此实现拖拽效果非常简单。(1).开始拖拽在拖拽元素上触发事件内只需要保存当前拖拽节点的信息即可onDragStart(e, treeNode) {this.dragOverStatus.dragNode {nodeData: treeNode.nodeData,parentNode: treeNode.parentNodeData};this.$emit(on-dragStart, {treeNode: treeNode.nodeData,parentNode: treeNode.parentNodeData,event: e});}复制代码(2).进入目标节点在目标元素上触发主要保存当前经过的节点的key然后向外层发出事件供组件调用者做其他操作。为了避免拖拽一个元素快速经过许多个节点时频繁发出事件设置定时器当停留一定时间后触发。onDragEnter(e, treeNode) {//当没有设置拖拽节点时禁止作为目标节点if (!this.hasDragNode()) {return;}this.dragOverStatus.overNodeKey ;//拖拽节点与目标节点是同一个return掉if (treeNode.nodeData._hash this.dragOverStatus.dragNode.nodeData._hash) {return;}this.dragOverStatus.overNodeKey treeNode.nodeData._hash; //当前经过的可放置的节点的key//当前节点禁止做为放置节点时if (treeNode.nodeData.noDrop) {return;}//设置dragEnter定时器停留250毫秒后触发事件if (!this.delayedDragEnterLogic) {this.delayedDragEnterLogic {};}Object.keys(this.delayedDragEnterLogic).forEach(key {clearTimeout(this.delayedDragEnterLogic[key]);});this.delayedDragEnterLogic[treeNode.nodeData._hash] setTimeout(() {if (!treeNode.nodeData.isExpand) {treeNode.toggleCollapseStatus();}this.$emit(on-dragEnter, {treeNode: treeNode.nodeData,parentNode: treeNode.parentNodeData,event: e});}, 250);}复制代码(3).在目标节点上经过在目标元素上触发即时计算鼠标在目标节点上的位置用于判断最终的放置位置0(作为目标节点的子节点)-1(放置在目标节点的前面)1(放置在目标节点的后面)显示相应的样式。onDragOver(e, treeNode) {//当没有设置拖拽节点时禁止作为目标节点if (!this.hasDragNode()) {return;}if (this.dragOverStatus.overNodeKey treeNode.nodeData._hash) {this.dragOverStatus.dropPosition this.calDropPosition(e); //放置标识0-1,1}this.$emit(on-dragOver, {treeNode: treeNode.nodeData,parentNode: treeNode.parentNodeData,event: e});this.dragOverClass this.setDragOverClass();//设置鼠标经过样式},复制代码当鼠标处于目标节点内目标节点偏上方(1/5处)则意为放在目标节点前面-同级当鼠标处于目标节点内目标节点偏下方(1/5处)意为放在目标节点后面-同级否则作为目标节点的子节点calDropPosition(e) {var offsetTop this.getOffset(e.target).top;var offsetHeight e.target.offsetHeight;var pageY e.pageY;var gapHeight 0.2 * offsetHeight;if (pageY offsetTop offsetHeight - gapHeight) {//放在目标节点后面-同级return 1;}if (pageY offsetTop gapHeight) {//放在目标节点前面-同级return -1;}//放在目标节点里面-作为子节点return 0;}复制代码(4).放置节点:在目标元素上触发此时将拖拽的信息变量作为参数将事件发射到外层其余操作由外层来决定即可。onDrop(e, treeNode) {//当没有设置拖拽节点时禁止作为目标节点if (!this.hasDragNode()) {return;}//当前节点禁止拖拽时if (treeNode.nodeData.noDrop) {return;}//拖拽节点与目标节点是同一个不做任何操作if (this.dragOverStatus.dragNode.nodeData._hash treeNode.nodeData._hash) {return;}var res {event: e,dragNode: this.dragOverStatus.dragNode,dropNode: {nodeData: treeNode.nodeData,parentNode: treeNode.parentNodeData},dropPosition: this.dragOverStatus.dropPosition};this.$emit(on-drop, res);}复制代码(5).拖拽结束:作用在拖拽元素上拖拽结束后将清除变量恢复样式。onDragEnd(e, treeNode) {//当没有设置拖拽节点时禁止作为目标节点if (!this.hasDragNode()) {return;}//当前节点禁止拖拽时if (treeNode.nodeData.noDrop) {return true;}this.dragOverStatus.dragNode null;this.dragOverStatus.overNodeKey ;this.$emit(on-dragEnd, {treeNode: treeNode.nodeData,parentNode: treeNode.parentNodeData,event: e});}复制代码4.应用调用树形拖拽组件获取拖拽过程中的拖拽节点目标节点以及放置位置具体处理拖拽结果由调用方决定可以是通过调接口更新树结构也可以由前端处理输入数据更新视图。复制代码getDropData(info) {var dragData info.dragNode.nodeData;var dragParent info.dragNode.parentNode;var dropData info.dropNode.nodeData;var dropParent info.dropNode.parentNode;var dropPosition info.dropPosition; //0作为子级-1放在目标节点前面1放在目标节点后面//把拖拽元素从父节点中删除dragParent.children.splice(dragParent.children.indexOf(dragData), 1);if (dropPosition 0) {dropData.children.push(dragData);} else {var index dropParent.children.indexOf(dropData);if (dropPosition -1) {dropParent.children.splice(index, 0, dragData);} else {dropParent.children.splice(index 1, 0, dragData);}}}复制代码作为子节点改变层级修改排序将拖拽节点放在目标节点后面修改排序将拖拽节点放在目标节点前面