实现的功能
- 节点信息图片展示
- 连线信息展示
- 连线动画效果
- 节点拖拽
- 缩放
- 移入节点展示更多信息
- 动态修改数据展示(包括添加和删除),修改vuedevtools试试看吧
- 因为公司内网的原因,不能照搬所有代码,只能实现部分。还有折叠,高亮,视图聚焦移动等,可以看看D3的示例。
感谢
巨人的肩膀。我是根据大佬的实现,进行了一些封装优化,我用的是最新的D3版本。
代码仓库
预览地址
一些功能的实现,详细请看源码
- render函数
render() {
this.container = this.svg.append("g").attr("class", "graph");
this.init();
// 每次迭代必须调用修改节点和边的位置
this.force.on("tick", () => {
this.initLinksPosition();
this.initNodesPosition();
});
}
init() {
this.initForce(); //初始化力模型
this.initMarker(); //初始化连线箭头
this.initLinks(); //连线需要在节点之前,否则会盖住
this.initNodes(); //初始化节点
this.initDrag(); //初始化节点拖拽
this.initZoom(); //初始化缩放
this.initNodeHover(); //初始化hover节点信息展示
}
- 初始化force
initForce() {
this.force = d3
.forceSimulation(this.nodes)
.alpha(this.alpha) //力迭代次数[0,1],越大布局越好
.force(
"charge",
d3.forceManyBody().strength(this.strength).distanceMax(this.distanceMax)
) //strength节点之间引力,负值越大分的越开。distanceMax连线距离
.force("link", d3.forceLink(this.links))
.force("center", d3.forceCenter(this.width / 2, this.height / 2)); //布局中心点
}
- 边箭头
initMarker() {
this.svg
.append("marker")
.attr("id", "resolved")
.attr("markerUnits", "userSpaceOnUse")
.attr("viewBox", "0 -5 10 10") //坐标系的区域
.attr("refX", 12) //箭头坐标
.attr("refY", 0)
.attr("markerWidth", 12) //标识的大小
.attr("markerHeight", 12)
.attr("orient", "auto") //绘制方向,可设定为:auto(自动确认方向)和 角度值
.attr("stroke-width", 2) //箭头宽度
.append("path")
.attr("d", "M0,-5L10,0L0,5") //箭头的路径
.attr("fill", "#6cbfed"); //箭头颜色
}
- 拖拽
initDrag() {
let _this = this;
this.graphNodes.call(
d3.drag().on("start", dragStart).on("drag", dragging).on("end", dragEnd)
);
function dragStart(d) {
if (!d.active) _this.force.alphaTarget(0.2).restart();
}
function dragging(d) {
d.subject.x = d.x;
d.subject.y = d.y;
}
function dragEnd(d) {
if (!d.active) _this.force.alphaTarget(0);
}
}
- 缩放
initZoom() {
// scaleExtent缩放范围
let _this = this;
this.svg.call(getZoom()).on("dblclick.zoom", null);//禁止双击放大
function getZoom() {
return (_this.zoom = d3
.zoom()
.scaleExtent([0.05, 8])//缩放大小限制
.on("zoom", (d) => {
d3.select("g.graph").attr("transform", function () {
return `translate(${d.transform.x},${d.transform.y}) scale(${d.transform.k})`;
});
}));
}
}
- 更新 reRender函数。更新千万不要append,要用select,否则更新一次,节点多一倍
reRender() {
this.reInitLink();
this.reInitNode();
this.force.nodes(this.nodes);
this.force.force("links", d3.forceLink(this.links));
this.force.alpha(this.alpha).alphaDecay(this.alphaDecay).restart();
}
扩展
我最后没有用D3去实现拓扑图可视化的展示。因为我们需要minimap展示,但是我写不出来😂。所以后面用到了另外一个框架–ANTV-G6。他封装了大量的操作和动画效果,还有minimap插件,上手比D3快的多。所以就采用了G6,完全可以实现一模一样的效果,并且是canvas绘制的。