如何用D3.js实现拓扑图

这篇文章主要介绍了关于如何用d3.js实现拓扑图,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下

最近写项目需要画出应用程序调用链的网路拓扑图,完全自己写需要花费些时间,那么首先想到的是echarts,但echarts的自定义写法写起来非常麻烦,而且它的文档都是基于配置说明的,对于自定义开发不太方便,尝试后果断放弃,改用D3.js,自己完全可控。

我们先看看效果

3420275523-5b37148dc6036_articlex[1].png

我把代码分享下,供和我一样刚接触D3的同学参考,不对的地方欢迎指正!

完整代码:

html

        Title                    body{            overflow: hidden;        }        #togo{            width: 800px;            height:500px;            border:1px solid #ccc;            user-select: none;        }        #togo text{            font-size:10px;/*和js里保持一致*/            fill:#1A2C3F;            text-anchor: middle;        }        #togo .node-other{            text-anchor: start;        }        #togo .health1{            stroke:#92E1A2;        }        #togo .health2{            stroke:orange;        }        #togo .health3{            stroke:red;        }        #togo #cloud,#togo #database{            fill:#ccc;        }        #togo .link{            stroke:#E4E8ED;        }        #togo .node-title{            font-size: 14px;        }        #togo .node-code circle{           fill:#3F86F5;        }        #togo .node-code text{            fill:#fff;        }        #togo .node-bg{            fill:#fff;        }        #togo .arrow{            fill:#E4E8ED;        }                  let t=new Togo('#togo',__options);    t.render(); 

登录后复制

JS:

const fontSize = 10;const symbolSize = 40;const padding = 10;/** 调用 new Togo(svg,option).render();* */class Togo {  /**/  constructor(svg, option) {    this.data = option.data;    this.edges = option.edges;    this.svg = d3.select(svg);  }  //主渲染方法  render() {    this.scale = 1;    this.width = this.svg.attr('width');    this.height = this.svg.attr('height');    this.container = this.svg.append('g')    .attr('transform', 'scale(' + this.scale + ')');    this.initPosition();    this.initDefineSymbol();    this.initLink();    this.initNode();    this.initZoom();  }  //初始化节点位置  initPosition() {    let origin = [this.width / 2, this.height / 2];    let points = this.getVertices(origin, Math.min(this.width, this.height) * 0.3, this.data.length);    this.data.forEach((item, i) => {      item.x = points[i].x;      item.y = points[i].y;    })  }  //根据多边形获取定位点  getVertices(origin, r, n) {    if (typeof n !== 'number') return;    var ox = origin[0];    var oy = origin[1];    var angle = 360 / n;    var i = 0;    var points = [];    var tempAngle = 0;    while (i  'marker-' + i)    .attr('markerUnits', 'userSpaceOnUse')    .attr('viewBox', '0 -5 10 10')    .attr('refX', symbolSize / 2 + padding)    .attr('refY', 0)    .attr('markerWidth', 14)    .attr('markerHeight', 14)    .attr('orient', 'auto')    .attr('stroke-width', 2)    .append('svg:path')    .attr('d', 'M2,0 L0,-3 L9,0 L0,3 M2,0 L0,-3')    .attr('class','arrow')    //数据库    let database =defs.append('g')      .attr('id','database')    .attr('transform','scale(0.042)');    database.append('path')    .attr('d','M512 800c-247.42 0-448-71.63-448-160v160c0 88.37 200.58 160 448 160s448-71.63 448-160V640c0 88.37-200.58 160-448 160z')    database.append('path')    .attr('d','M512 608c-247.42 0-448-71.63-448-160v160c0 88.37 200.58 160 448 160s448-71.63 448-160V448c0 88.37-200.58 160-448 160z') ;    database.append('path')    .attr('d','M512 416c-247.42 0-448-71.63-448-160v160c0 88.37 200.58 160 448 160s448-71.63 448-160V256c0 88.37-200.58 160-448 160z') ;    database.append('path')    .attr('d','M64 224a448 160 0 1 0 896 0 448 160 0 1 0-896 0Z');    //云    let cloud=defs.append('g')    .attr('id','cloud')    .attr('transform','scale(0.042)')    .append('path')    .attr('d','M709.3 285.8C668.3 202.7 583 145.4 484 145.4c-132.6 0-241 102.8-250.4 233-97.5 27.8-168.5 113-168.5 213.8 0 118.9 98.8 216.6 223.4 223.4h418.9c138.7 0 251.3-118.8 251.3-265.3 0-141.2-110.3-256.2-249.4-264.5z')  }  //初始化链接线  initLink() {    this.drawLinkLine();    this.drawLinkText();  }  //初始化节点  initNode() {    var self = this;    //节点容器    this.nodes = this.container.selectAll(".node")    .data(this.data)    .enter()    .append("g")    .attr("transform", function (d) {      return "translate(" + d.x + "," + d.y + ")";    })    .call(d3.drag()      .on("drag", function (d) {        self.onDrag(this, d)      })    )    .on('click', function () {      alert()    })    //节点背景默认覆盖层    this.nodes.append('circle')    .attr('r', symbolSize / 2 + padding)    .attr('class', 'node-bg');    //节点图标    this.drawNodeSymbol();    //节点标题    this.drawNodeTitle();    //节点其他说明    this.drawNodeOther();    this.drawNodeCode();  }  //画节点语言标识  drawNodeCode() {    this.nodeCodes = this.nodes.filter(item => item.type == 'app')    .append('g')    .attr('class','node-code')    .attr('transform', 'translate(' + -symbolSize / 2 + ',' + symbolSize / 3 + ')')    this.nodeCodes    .append('circle')    .attr('r', d => fontSize / 2 * d.code.length / 2 + 3)    this.nodeCodes    .append('text')    .attr('dy', fontSize / 2)    .text(item => item.code);  }  //画节点图标  drawNodeSymbol() {    //绘制节点    this.nodes.filter(item=>item.type=='app')    .append("circle")    .attr("r", symbolSize / 2)    .attr("fill", '#fff')    .attr('class', function (d) {      return 'health'+d.health;    })    .attr('stroke-width', '5px')    this.nodes.filter(item=>item.type=='database')    .append('use')    .attr('xlink:href','#database')    .attr('x',function () {      return -this.getBBox().width/2    })    .attr('y',function () {      return -this.getBBox().height/2    })    this.nodes.filter(item=>item.type=='cloud')    .append('use')    .attr('xlink:href','#cloud')    .attr('x',function () {      return -this.getBBox().width/2    })    .attr('y',function () {      return -this.getBBox().height/2    })  }  //画节点右侧信息  drawNodeOther() {    //如果是应用的时候    this.nodeOthers = this.nodes.filter(item => item.type == 'app')    .append("text")    .attr("x", symbolSize / 2 + padding)    .attr("y", -5)    .attr('class','node-other')    this.nodeOthers.append('tspan')    .text(d => d.time + 'ms');    this.nodeOthers.append('tspan')    .text(d => d.rpm + 'rpm')    .attr('x', symbolSize / 2 + padding)    .attr('dy', '1em');    this.nodeOthers.append('tspan')    .text(d => d.epm + 'epm')    .attr('x', symbolSize / 2 + padding)    .attr('dy', '1em')  }  //画节点标题  drawNodeTitle() {    //节点标题    this.nodes.append("text")    .attr('class','node-title')    .text(function (d) {      return d.name;    })    .attr("dy", symbolSize)    this.nodes.filter(item => item.type == 'app').append("text")    .text(function (d) {      return d.active + '/' + d.total;    })    .attr('dy', fontSize / 2)    .attr('class','node-call')  }  //画节点链接线  drawLinkLine() {    let data = this.data;    if (this.lineGroup) {      this.lineGroup.selectAll('.link')      .attr(        'd', link => genLinkPath(link),      )    } else {      this.lineGroup = this.container.append('g')      this.lineGroup.selectAll('.link')      .data(this.edges)      .enter()      .append('path')      .attr('class', 'link')      .attr(        'marker-end', (link, i) => 'url(#' + 'marker-' + i + ')'      ).attr(        'd', link => genLinkPath(link),      ).attr(        'id', (link, i) => 'link-' + i      )      .on('click', () => { alert() })    }    function genLinkPath(d) {      let sx = data[d.source].x;      let tx = data[d.target].x;      let sy = data[d.source].y;      let ty = data[d.target].y;      return 'M' + sx + ',' + sy + ' L' + tx + ',' + ty;    }  }  drawLinkText() {    let data = this.data;    let self = this;    if (this.lineTextGroup) {      this.lineTexts      .attr('transform', getTransform)    } else {      this.lineTextGroup = this.container.append('g')      this.lineTexts = this.lineTextGroup      .selectAll('.linetext')      .data(this.edges)      .enter()      .append('text')      .attr('dy', -2)      .attr('transform', getTransform)      .on('click', () => { alert() })      this.lineTexts      .append('tspan')      .text((d, i) => this.data[d.source].lineTime + 'ms,' + this.data[d.source].lineRpm + 'rpm');      this.lineTexts      .append('tspan')      .text((d, i) => this.data[d.source].lineProtocol)      .attr('dy', '1em')      .attr('dx', function () {        return -this.getBBox().width / 2      })    }    function getTransform(link) {      let s = data[link.source];      let t = data[link.target];      let p = self.getCenter(s.x, s.y, t.x, t.y);      let angle = self.getAngle(s.x, s.y, t.x, t.y);      if (s.x > t.x && s.y < t.y || s.x  t.y) {        angle = -angle      }      return 'translate(' + p[0] + ',' + p[1] + ') rotate(' + angle + ')'    }  }  update(d) {    this.drawLinkLine();    this.drawLinkText();  }  //拖拽方法  onDrag(ele, d) {    d.x = d3.event.x;    d.y = d3.event.y;    d3.select(ele)    .attr('transform', "translate(" + d3.event.x + "," + d3.event.y + ")")    this.update(d);  }  //缩放方法  onZoom(ele) {    var transform = d3.zoomTransform(ele);    this.scale = transform.k;    this.container.attr('transform', "translate(" + transform.x + "," + transform.y + ")scale(" + transform.k + ")")  }}

登录后复制

数据:

let __options={  data:[{    type:'app',    name: 'monitor-web-server',    time: 30,    rpm: 40,    epm: 50,    active: 3,    total: 5,    code: 'java',    health: 1,    lineProtocol: 'http',    lineTime: 12,    lineRpm: 34,  }, {    type:'database',    name: 'Mysql',    time: 30,    rpm: 40,    epm: 50,    active: 3,    total: 5,    code: 'java',    health: 2,    lineProtocol: 'http',    lineTime: 12,    lineRpm: 34,  },    {      type:'app',      name: 'Redis',      time: 30,      rpm: 40,      epm: 50,      active: 3,      total: 5,      code: 'java',      health: 3,      lineProtocol: 'http',      lineTime: 12,      lineRpm: 34,    }, {      type:'cloud',      name: 'ES',      time: 30,      rpm: 40,      epm: 50,      active: 3,      total: 5,      code: 'java',      health: 1,      lineProtocol: 'http',      lineTime: 12,      lineRpm: 34,      value: 100    }  ],  edges: [     {      source: 0,      target: 3,    }, {      source: 1,      target: 2,    }    , {      source: 1,      target: 3,    },    {      source: 0,      target: 1,    },    {      source: 0,      target: 2,    }    // {    //   source: 3,    //   target: 2,    // },  ]}

登录后复制

以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!

相关推荐:

js将任意元素移动到指定位置

以上就是如何用D3.js实现拓扑图的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2746542.html

(0)
上一篇 2025年3月8日 04:09:23
下一篇 2025年3月3日 08:08:48

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • 关于JS中new调用函数的原理介绍

    这篇文章主要介绍了关于js中new调用函数的原理介绍,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下 JavaScript 中经常使用构造函数创建对象(通过 new 操作符调用一个函数),那在使用 new 调用一个函数的时候到…

    编程技术 2025年3月8日
    200
  • 基于vue.js的dialog插件art-dialog-vue2.0的发布内容

    这篇文章主要介绍了关于基于vue.js的dialog插件art-dialog-vue2.0的发布内容,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下 art-dialog-vue —— 经典、优雅的网页对话框控件 优点 支持普…

    编程技术 2025年3月8日
    200
  • 关于Angular中响应式表单的介绍

    这篇文章主要介绍了关于angular中响应式表单的介绍,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下 响应式表单是在组件类中编写逻辑,验证规则,这与在模板中完成控制的模板驱动式表单不同。响应式表单反应形式是灵活的,可用于处理…

    编程技术 2025年3月8日
    200
  • 创建quick-shell.js库的使用介绍

    这篇文章主要介绍了关于创建quick-shell.js库的使用介绍,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下 quick-shell.js简介 一直想自己发布一个npm包试试,正巧刚刚学完操作系统,写了很多shell类型…

    编程技术 2025年3月8日
    200
  • 关于react父子组件之间的传值问题解析

    这篇文章主要介绍了关于react父子组件之间的传值问题解析,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下 父组件传递给子组件:核心思路就是将父组件中的state传递给子组件 父组件代码:class Father extend…

    编程技术 2025年3月8日
    200
  • 如何用JavaScript实现一个栈

    这篇文章主要介绍了关于如何用javascript实现一个栈,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下 什么是栈(Stack) 栈是一种遵从后进先出(LIFO)原则的有序集合。 新添加的或待删除的元素都保存在栈的末尾,称为…

    2025年3月8日
    200
  • JavaScript中Array数组的tips的讲解

    这篇文章主要介绍了关于javascript中array数组的tips的讲解,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下 1. Array.prototype.push() 像数组一样使用对象: var obj = { le…

    编程技术 2025年3月8日
    200
  • 对于Angular template syntax的讲解

    这篇文章主要介绍了关于对angular template syntax的讲解,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下 模板语法简介 1、插值表达式 Hello {{name}} 登录后复制 Angular 对所有双花括…

    编程技术 2025年3月8日
    200
  • 关于vue中extend,mixins,extends,components,install的操作

    这篇文章主要介绍了关于vue中extend,mixins,extends,components,install的操作,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下 前言 你知道extend,mixins,extends,co…

    编程技术 2025年3月8日
    200
  • jQuery+AJAX+PHP+MySQL开发搜索无跳转无刷新的功能

    这篇文章主要介绍了关于jquery+ajax+php+mysql开发搜索无跳转无刷新的功能,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下 知识点:ajax提交表单,php查询数据库,php返回json数组,javascrip…

    2025年3月8日 编程技术
    200

发表回复

登录后才能评论