Home Web Front-end JS Tutorial How to implement topology diagram with D3.js

How to implement topology diagram with D3.js

Jul 10, 2018 pm 05:27 PM
html html5 javascript

This article mainly introduces how to use D3.js to implement topology diagrams. It has certain reference value. Now I share it with everyone. Friends in need can refer to it.

Recently writing projects require drawing applications. It takes some time to write the network topology diagram of the program call chain by yourself. The first thing that comes to mind is echarts, but the custom writing method of echarts is very troublesome to write, and its documents are all based on configuration instructions. For custom development It was not very convenient, so I gave up after trying it and switched to D3.js, which is completely under my control.

Let’s take a look at the effect first

How to implement topology diagram with D3.js

I will share the code for reference by students who are new to D3 like me. If you find something wrong, please feel free to comment. Correction!

Complete code:

html:

nbsp;html>


    <meta>
    <title>Title</title>
    <script>
    </script>
    <style>
        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;
        }
    </style>
    <script></script>


 <svg>

 </svg>
 <script></script>
 <script>

 </script>

 <script>
    let t=new Togo(&#39;#togo&#39;,__options);
    t.render();
 </script>



Copy after login

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) {
        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 + ")")
  }

}
Copy after login

Data:

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,
    // },
  ]
}
Copy after login

The above is the entire content of this article, I hope It will be helpful for everyone’s learning. For more related content, please pay attention to the PHP Chinese website!

Related recommendations:

js moves any element to a specified position

The above is the detailed content of How to implement topology diagram with D3.js. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Table Border in HTML Table Border in HTML Sep 04, 2024 pm 04:49 PM

Guide to Table Border in HTML. Here we discuss multiple ways for defining table-border with examples of the Table Border in HTML.

Nested Table in HTML Nested Table in HTML Sep 04, 2024 pm 04:49 PM

This is a guide to Nested Table in HTML. Here we discuss how to create a table within the table along with the respective examples.

HTML margin-left HTML margin-left Sep 04, 2024 pm 04:48 PM

Guide to HTML margin-left. Here we discuss a brief overview on HTML margin-left and its Examples along with its Code Implementation.

HTML Table Layout HTML Table Layout Sep 04, 2024 pm 04:54 PM

Guide to HTML Table Layout. Here we discuss the Values of HTML Table Layout along with the examples and outputs n detail.

HTML Input Placeholder HTML Input Placeholder Sep 04, 2024 pm 04:54 PM

Guide to HTML Input Placeholder. Here we discuss the Examples of HTML Input Placeholder along with the codes and outputs.

HTML Ordered List HTML Ordered List Sep 04, 2024 pm 04:43 PM

Guide to the HTML Ordered List. Here we also discuss introduction of HTML Ordered list and types along with their example respectively

Moving Text in HTML Moving Text in HTML Sep 04, 2024 pm 04:45 PM

Guide to Moving Text in HTML. Here we discuss an introduction, how marquee tag work with syntax and examples to implement.

HTML onclick Button HTML onclick Button Sep 04, 2024 pm 04:49 PM

Guide to HTML onclick Button. Here we discuss their introduction, working, examples and onclick Event in various events respectively.

See all articles