vue实现节点连线
Vue 实现节点连线的方法
使用 SVG 和动态计算
在 Vue 中可以通过 SVG 实现节点间的连线。SVG 的 <path> 或 <line> 元素可用于绘制连接线,动态计算节点位置并更新连线路径。
<template>
<svg width="100%" height="500">
<!-- 节点 -->
<circle
v-for="(node, index) in nodes"
:key="index"
:cx="node.x"
:cy="node.y"
r="20"
fill="blue"
@mousedown="startDrag(index, $event)"
@mousemove="dragNode($event)"
@mouseup="stopDrag"
/>
<!-- 连线 -->
<path
v-for="(link, index) in links"
:key="'link-' + index"
:d="calculatePath(link)"
stroke="black"
fill="none"
/>
</svg>
</template>
<script>
export default {
data() {
return {
nodes: [
{ x: 100, y: 100 },
{ x: 300, y: 200 }
],
links: [
{ source: 0, target: 1 }
],
dragging: null
};
},
methods: {
calculatePath(link) {
const source = this.nodes[link.source];
const target = this.nodes[link.target];
return `M ${source.x} ${source.y} L ${target.x} ${target.y}`;
},
startDrag(index, event) {
this.dragging = index;
},
dragNode(event) {
if (this.dragging !== null) {
const rect = event.target.getBoundingClientRect();
this.nodes[this.dragging].x = event.clientX - rect.left;
this.nodes[this.dragging].y = event.clientY - rect.top;
}
},
stopDrag() {
this.dragging = null;
}
}
};
</script>
使用第三方库(jsPlumb)
jsPlumb 是一个专门用于实现节点连线的库,支持拖拽、连线样式自定义等功能。

安装 jsPlumb:

npm install jsplumb
在 Vue 中使用:
<template>
<div class="container">
<div
v-for="(node, index) in nodes"
:key="index"
:id="'node-' + index"
class="node"
>
Node {{ index }}
</div>
</div>
</template>
<script>
import { jsPlumb } from 'jsplumb';
export default {
mounted() {
this.initJsPlumb();
},
data() {
return {
nodes: [ {}, {} ]
};
},
methods: {
initJsPlumb() {
jsPlumb.ready(() => {
jsPlumb.connect({
source: 'node-0',
target: 'node-1',
anchors: ['Right', 'Left'],
connector: ['Straight'],
endpoint: 'Blank'
});
});
}
}
};
</script>
<style>
.container {
position: relative;
height: 500px;
}
.node {
position: absolute;
width: 100px;
height: 50px;
border: 1px solid #ccc;
padding: 10px;
}
#node-0 { left: 50px; top: 100px; }
#node-1 { left: 300px; top: 200px; }
</style>
使用 Canvas 和动态渲染
通过 Canvas 可以实现高性能的节点连线,适合大量节点的场景。
<template>
<canvas ref="canvas" width="800" height="500" @mousedown="handleMouseDown"></canvas>
</template>
<script>
export default {
data() {
return {
nodes: [
{ x: 100, y: 100 },
{ x: 300, y: 200 }
],
links: [
{ source: 0, target: 1 }
],
selectedNode: null
};
},
mounted() {
this.drawCanvas();
},
methods: {
drawCanvas() {
const canvas = this.$refs.canvas;
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 绘制连线
ctx.strokeStyle = 'black';
this.links.forEach(link => {
const source = this.nodes[link.source];
const target = this.nodes[link.target];
ctx.beginPath();
ctx.moveTo(source.x, source.y);
ctx.lineTo(target.x, target.y);
ctx.stroke();
});
// 绘制节点
ctx.fillStyle = 'blue';
this.nodes.forEach(node => {
ctx.beginPath();
ctx.arc(node.x, node.y, 20, 0, Math.PI * 2);
ctx.fill();
});
},
handleMouseDown(event) {
const rect = this.$refs.canvas.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
this.nodes.forEach((node, index) => {
const distance = Math.sqrt((x - node.x) 2 + (y - node.y) 2);
if (distance <= 20) {
this.selectedNode = index;
document.addEventListener('mousemove', this.handleMouseMove);
document.addEventListener('mouseup', this.handleMouseUp);
}
});
},
handleMouseMove(event) {
if (this.selectedNode !== null) {
const rect = this.$refs.canvas.getBoundingClientRect();
this.nodes[this.selectedNode].x = event.clientX - rect.left;
this.nodes[this.selectedNode].y = event.clientY - rect.top;
this.drawCanvas();
}
},
handleMouseUp() {
this.selectedNode = null;
document.removeEventListener('mousemove', this.handleMouseMove);
document.removeEventListener('mouseup', this.handleMouseUp);
}
}
};
</script>
注意事项
- 动态计算连线路径时,需考虑节点的位置变化,实时更新连线。
- 使用第三方库(如 jsPlumb)时,注意在组件销毁时清理资源。
- Canvas 方案需手动处理交互和渲染,适合复杂场景。






