SVGElement Drag & Drop

本文介绍了一种使用JavaScript实现SVG图形元素拖拽的方法。通过监听mousedown、mousemove和mouseup事件,实现了SVG圆形元素的位置调整,并实时更新其坐标属性。
<html>
<script>
var dx, dy;
var x, y;
var actived = null;
function mouseDownListener(e){
    if(actived.setCapture){
        actived.setCapture();
    }
    else{
        window.captureEvents(Event.MOUSEMOVE | Event.MOUSEUP);
    }
    var canvas = document.getElementById('objCanvas');
    canvas.addEventListener("mousemove", mouseMoveListener, false);
    canvas.addEventListener("mouseup", mouseUpListener, false);
    x = e.offsetX, y = e.offsetY;
    dx = x-parseInt(actived.getAttribute('cx'), 10);
    dy = y-parseInt(actived.getAttribute('cy'), 10);
    console.log('('+x+','+y+'), ('+dx+','+dy+')');
    //console.log('mouse down | X,Y: ('+e.x+', '+e.y+') | client X,Y: (' + e.clientX + ', ' + e.clientY + ') offset X,Y: (' + e.offsetX + ', ' + e.offsetY + ')');
    //e.cancelBubble = true;
}

function mouseMoveListener(e){
    //actived.setAttribute("transform", "translate("+dx+", "+dy+")");
    actived.setAttribute("cx", e.offsetX-dx);
    actived.setAttribute("cy", e.offsetY-dy);
    //console.log('mouse moving: ('+e.clientX+', '+e.clientY+')('+e.x+', '+dy+')');
}

function mouseUpListener(e){
    dragging = false;
    console.log('mouse up');
    if(actived.releaseCapture){
        actived.releaseCapture();
    }
    else{
        window.releaseEvents(Event.MOUSEMOVE|Event.MOUSEUP);
    }
    //actived.setAttribute('cx', x);
    //actived.setAttribute('cy', y);
    //actived.setAttribute('transform', 'translate(0, 0)');


    var canvas = document.getElementById('objCanvas');
    canvas.removeEventListener('mousemove', mouseMoveListener);
    canvas.removeEventListener('mouseup', mouseUpListener);
    //canvas.onmousemove  = null;
    //canvas.onmouseup = null;

}

document.onreadystatechange = function(e){
    if(document.readyState=='complete'){
        actived = document.getElementById('objCircle');
        //actived.onmousedown = mouseDownListener;
        actived.addEventListener("mousedown", mouseDownListener, false);
    }
}
</script>
<body>
<svg id="objCanvas" width="640" height="480" style="background-color:#eee" version="1.1"
xmlns="http://www.w3.org/2000/svg">

<circle id="objCircle" cx="100" cy="100" r="5" stroke="green"
stroke-width="2" fill="white"/>

</svg>
</body>
</html>
我现在是这样写的:&lt;el-tree id=&quot;ccw_device_tree&quot; v-show=&quot;filterText == null || filterText == &#39;&#39;&quot; ref=&quot;treeRef&quot; :key=&quot;treeRefKey&quot; node-key=&quot;id&quot; :props=&quot;defaultProps&quot; :default-expanded-keys=&quot;defaultExpendKeys&quot; @node-click=&quot;treeNodeClick&quot; @node-contextmenu=&quot;rightClick&quot; highlight-current :load=&quot;loadNode&quot; :lazy=&quot;isLazyTree&quot; :data=&quot;treeNoLazyList&quot; v-loading=&quot;treeParam.isLoading&quot; class=&quot;deviceTreeTab&quot; :allow-drop=&quot;allowDrop&quot; @node-drop=&quot;handleNodeDrop&quot; &gt; &lt;template #default=&quot;scope&quot;&gt; &lt;template v-if=&quot;scope.data.subId === &#39;loadmore&#39;&quot;&gt; &lt;span v-if=&quot;!scope.data.loading&quot; class=&quot;tree-node loadmore&quot; @click.stop=&quot;loadMoreNode(scope, &#39;group&#39;)&quot; &gt; &lt;span class=&quot;loadmore-text&quot;&gt; &lt;span&gt;{{ scope.data.name }}&lt;/span&gt; &lt;img src=&quot;@/assets/imgs/ico_tree_more.png&quot; /&gt; &lt;/span&gt; &lt;/span&gt; &lt;span class=&quot;tree-node loadmore&quot; v-else &gt;&lt;span class=&quot;loadmore-text&quot;&gt;正在加载中...&lt;/span&gt;&lt;/span &gt; &lt;/template&gt; &lt;template v-else&gt; &lt;div :id=&quot;&#39;tree&#39; + scope.data.id&quot; class=&quot;tree_node_content&quot; :draggable=&quot;isDraggable &amp;&amp; scope.data.online != 0&quot; @dragstart=&quot;dragItem = scope.data&quot; @dragend=&quot;dragItem = null&quot; &gt; &lt;!-- 无经纬度设备标记 --&gt; &lt;img style=&quot;width: 15px; height: 15px&quot; v-if=&quot; (scope.data.type == 3 || scope.data.type == &#39;IPC&#39;) &amp;&amp; switchValue &amp;&amp; !scope.data.longitude &amp;&amp; !scope.data.latitude &quot; src=&quot;@/assets/imgs/errorPosition.svg&quot; alt=&quot;&quot; /&gt; &lt;!--0:本平台--&gt; &lt;sc-icon-device-tree-platform v-if=&quot;scope.data.type == 0&quot; /&gt; &lt;!--1:分组--&gt; &lt;template v-if=&quot;scope.data.type == 1&quot;&gt; &lt;sc-icon-device-tree-area-e v-if=&quot;scope.node.expanded&quot; /&gt; &lt;sc-icon-device-tree-area v-else /&gt; &lt;/template&gt; &lt;!--3:设备,枪机--&gt; &lt;sc-icon-device-tree-ipc v-if=&quot; (scope.data.type == 3 || scope.data.type == &#39;IPC&#39;) &amp;&amp; scope.data.online == 1 &amp;&amp; !( scope.data.subType != null &amp;&amp; (scope.data.subType == 1 || scope.data.subType == 2) ) &quot; /&gt; &lt;sc-icon-device-tree-ipc-offline v-if=&quot; (scope.data.type == 3 || scope.data.type == &#39;IPC&#39;) &amp;&amp; scope.data.online == 0 &amp;&amp; !( scope.data.subType != null &amp;&amp; (scope.data.subType == 1 || scope.data.subType == 2) ) &quot; /&gt; &lt;!--3:设备,球机--&gt; &lt;sc-icon-device-tree-ipc2 v-if=&quot; (scope.data.type == 3 || scope.data.type == &#39;IPC&#39;) &amp;&amp; scope.data.online == 1 &amp;&amp; scope.data.subType != null &amp;&amp; scope.data.subType == 1 &quot; /&gt; &lt;sc-icon-device-tree-ipc2-offline v-if=&quot; (scope.data.type == 3 || scope.data.type == &#39;IPC&#39;) &amp;&amp; scope.data.online == 0 &amp;&amp; scope.data.subType != null &amp;&amp; scope.data.subType == 1 &quot; /&gt; &lt;!--3:设备,半球机--&gt; &lt;sc-icon-device-tree-ipc3 v-if=&quot; (scope.data.type == 3 || scope.data.type == &#39;IPC&#39;) &amp;&amp; scope.data.online == 1 &amp;&amp; scope.data.subType != null &amp;&amp; scope.data.subType == 2 &quot; /&gt; &lt;sc-icon-device-tree-ipc3-offline v-if=&quot; (scope.data.type == 3 || scope.data.type == &#39;IPC&#39;) &amp;&amp; scope.data.online == 0 &amp;&amp; scope.data.subType != null &amp;&amp; scope.data.subType == 2 &quot; /&gt; &lt;!--4:nvr--&gt; &lt;sc-icon-device-tree-nvr v-if=&quot;scope.data.online == 1 &amp;&amp; scope.data.type == 4&quot; /&gt; &lt;sc-icon-device-tree-nvr-offline v-if=&quot;scope.data.online == 0 &amp;&amp; scope.data.type == 4&quot; /&gt; &lt;sc-icon-voice-tag v-if=&quot;scope.data.supportPickup == 1&quot; &gt;&lt;/sc-icon-voice-tag&gt; &lt;span class=&quot;tree_node_label&quot; v-if=&quot;$route.path == &#39;/monitor/live&#39;&quot; &gt;{{ scope.data.name + (scope.data.descendantSize != null &amp;&amp; scope.data.descendantSize.total != null &amp;&amp; scope.data.descendantSize.online != null &amp;&amp; filterisOnlineValue == &quot;&quot; &amp;&amp; scope.data.type != 3 ? &quot; (&quot; + scope.data.descendantSize.online + &quot;/&quot; + scope.data.descendantSize.total + &quot;)&quot; : &quot;&quot;) }}&lt;/span &gt; &lt;span class=&quot;tree_node_label&quot; v-else-if=&quot;$route.path == &#39;/equipment/video/local&#39;&quot; &gt;{{ scope.data.name + (scope.data.descendantSize != null &amp;&amp; scope.data.descendantSize.total != null &amp;&amp; scope.data.descendantSize.online != null &amp;&amp; filterisStorageValue == &quot;&quot; &amp;&amp; scope.data.type != 3 ? &quot; (&quot; + scope.data.descendantSize.online + &quot;/&quot; + scope.data.descendantSize.total + &quot;)&quot; : &quot;&quot;) }}&lt;/span &gt; &lt;span class=&quot;tree_node_label&quot; v-else&gt;{{ scope.data.name + (scope.data.descendantSize != null &amp;&amp; scope.data.descendantSize.total != null &amp;&amp; scope.data.descendantSize.online != null &amp;&amp; scope.data.type != 3 ? &quot; (&quot; + scope.data.descendantSize.online + &quot;/&quot; + scope.data.descendantSize.total + &quot;)&quot; : &quot;&quot;) }}&lt;/span&gt; &lt;el-icon v-if=&quot;![&#39;3&#39;, &#39;IPC&#39;].includes(scope.data.type)&quot; @click.stop=&quot;nodeReload(scope.node)&quot; &gt;&lt;el-icon-refresh /&gt;&lt;/el-icon&gt; &lt;sc-icon-storage-recording v-if=&quot; isShowStorageIcon &amp;&amp; (scope.data.type == 3 || scope.data.type == &#39;IPC&#39;) &amp;&amp; scope.data.online != 0 &amp;&amp; scope.data.storageType == 1 &amp;&amp; scope.data.storageStatus == 1 &quot; class=&quot;tree_node_content_storage_icon&quot; /&gt; &lt;sc-icon-storage-file-store v-else-if=&quot; isShowStorageIcon &amp;&amp; (scope.data.type == 3 || scope.data.type == &#39;IPC&#39;) &amp;&amp; scope.data.online != 0 &amp;&amp; scope.data.storageType == 1 &amp;&amp; scope.data.storageStatus != 1 &amp;&amp; scope.data.storageFileStatus == 1 &quot; class=&quot;tree_node_content_storage_icon&quot; /&gt; &lt;sc-icon-storage-no-do v-else-if=&quot; isShowStorageIcon &amp;&amp; (scope.data.type == 3 || scope.data.type == &#39;IPC&#39;) &amp;&amp; scope.data.storageType == 1 &quot; class=&quot;tree_node_content_storage_icon&quot; /&gt; &lt;/div&gt; &lt;/template&gt; &lt;/template&gt; &lt;/el-tree&gt; 在js里面定义了const allowDrop = (draggingNode, dropNode, type) =&gt; { // 只允许目录节点(type=1)之间排序 return draggingNode.data.type === 1 &amp;&amp; dropNode.data.type === 1 &amp;&amp; type === &#39;inner&#39;; }; // 处理节点放置事件 const handleNodeDrop = async (draggingNode, dropNode, dropType) =&gt; { console.log(&#39;你好,拖拽完成了吗&#39;, draggingNode, dropNode, dropType); // 确保操作的是目录节点 if (draggingNode.data.type !== 1 || dropNode.data.type !== 1) return; // 获取父节点数据 const parent = dropNode.parent; const children = parent.data.children || parent.data; // 重新排序子节点 const oldIndex = children.findIndex(c =&gt; c.id === draggingNode.data.id); children.splice(oldIndex, 1); const newIndex = children.findIndex(c =&gt; c.id === dropNode.data.id); children.splice(dropType === &#39;before&#39; ? newIndex : newIndex + 1, 0, draggingNode.data); // // 调用后端接口保存新顺序 // try { // await api.updateGroupOrder({ // parentId: parent.data.id, // orderList: children.map((item, index) =&gt; ({ id: item.id, order: index })) // }); // } catch (error) { // // 回滚本地数据 // children.splice(newIndex, 1); // children.splice(oldIndex, 0, draggingNode.data); // ElMessage.error(&#39;顺序更新失败&#39;); // } }; 目前是要分组能拖动排序。但是我自己拖完以后,没有看到变化不说,console.log(&#39;你好,拖拽完成了吗&#39;, draggingNode, dropNode, dropType);这一行的输出也没有看到,是怎么回事呢?
05-29
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值