亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

D3.js實現拓撲圖的示例代碼

2019-11-19 13:35:07
字體:
來源:轉載
供稿:網友

最近寫項目需要畫出應用程序調用鏈的網路拓撲圖,完全自己寫需要花費些時間,那么首先想到的是echarts,但echarts的自定義寫法寫起來非常麻煩,而且它的文檔都是基于配置說明的,對于自定義開發不太方便,嘗試后果斷放棄,改用D3.js,自己完全可控。

我們先看看效果

我把代碼分享下,供和我一樣剛接觸D3的同學參考,不對的地方歡迎指正!

完整代碼:

html:

<!DOCTYPE html><html lang="en"><head>  <meta charset="UTF-8">  <title>Title</title>  <script type="text/javascript" src="http://d3js.org/d3.v5.min.js">  </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 src="data.js"></script></head><body> <svg id="togo" width="800" height="500"> </svg> <script src="togo.js"></script> <script> </script> <script>  let t=new Togo('#togo',__options);  t.render(); </script></body></html>

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 < n) {   tempAngle = (i * angle * Math.PI) / 180;   points.push({    x: ox + r * Math.sin(tempAngle),    y: oy + r * Math.cos(tempAngle),   });   i++;  }  return points; } //兩點的中心點 getCenter(x1, y1, x2, y2) {  return [(x1 + x2) / 2, (y1 + y2) / 2] } //兩點的距離 getDistance(x1, y1, x2, y2) {  return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)); } //兩點角度 getAngle(x1, y1, x2, y2) {  var x = Math.abs(x1 - x2);  var y = Math.abs(y1 - y2);  var z = Math.sqrt(x * x + y * y);  return Math.round((Math.asin(y / z) / Math.PI * 180)); } //初始化縮放器 initZoom() {  let self = this;  let zoom = d3.zoom()  .scaleExtent([0.7, 3])  .on('zoom', function () {   self.onZoom(this)  });  this.svg.call(zoom) } //初始化圖標 initDefineSymbol() {  let defs=this.container.append('svg:defs');  //箭頭  const marker = defs  .selectAll('marker')  .data(this.edges)  .enter()  .append('svg:marker')  .attr('id', (link, 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.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 + ")") }}

數據:

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

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品久久99久久| 亚洲欧美一区二区三区情侣bbw| www.国产精品一二区| 91av在线网站| 色综合久久天天综线观看| 亚洲精品天天看| 国产综合在线视频| 在线观看成人黄色| 亚洲综合成人婷婷小说| 欧美黑人极品猛少妇色xxxxx| 国产欧美精品日韩| 亚洲天堂免费观看| 欧美精品一区二区三区国产精品| 欧美一区二区视频97| 日韩欧美在线视频免费观看| 欧美性jizz18性欧美| 日韩精品中文字幕视频在线| 日韩久久精品电影| 国产综合香蕉五月婷在线| 欧美国产欧美亚洲国产日韩mv天天看完整| 久久躁日日躁aaaaxxxx| 亚洲在线第一页| 91最新在线免费观看| 夜色77av精品影院| 国内伊人久久久久久网站视频| 亚洲国产99精品国自产| 亚洲欧洲在线免费| 亚洲国产精久久久久久久| 日本高清久久天堂| 欧美亚洲伦理www| 亚洲视频777| 欧美与黑人午夜性猛交久久久| 亚洲 日韩 国产第一| 大量国产精品视频| 成人国内精品久久久久一区| 亚洲18私人小影院| 国产精品xxx视频| 亚洲国产精品久久久| 琪琪亚洲精品午夜在线| 日本高清视频一区| 欧美国产日韩xxxxx| 欧美性xxxxx极品| 精品欧美激情精品一区| 欧美色视频日本高清在线观看| 亚洲福利视频久久| 欧美在线一级va免费观看| 欧美激情视频在线观看| 欧美成人黑人xx视频免费观看| 欧美激情精品久久久久久大尺度| 欧美一区二区三区四区在线| 这里精品视频免费| 97色在线观看免费视频| 亚洲第一精品久久忘忧草社区| 日韩欧美亚洲一二三区| 日韩精品在线观看网站| 欧美日韩性视频| 国产精品亚洲综合天堂夜夜| 日韩中文在线不卡| 欧美激情欧美激情在线五月| 国产精品免费小视频| 亚洲美女中文字幕| 国产性猛交xxxx免费看久久| 中文字幕欧美精品在线| 欧美激情亚洲国产| 精品久久久久久久久久久久久| 亚洲激情视频在线| 欧美一级片久久久久久久| 亚洲精品一区二三区不卡| 国产精品久久97| 国产日韩欧美在线看| 在线观看视频亚洲| 欧美成人免费大片| 国产精品久久久久久久久| 精品视频在线播放色网色视频| 欧美日韩美女视频| 97色伦亚洲国产| 国产91精品久久久久| 国产精品吹潮在线观看| 一区二区在线视频| 欧美性猛交视频| 日韩中文字幕在线观看| 欧美性xxxx极品hd欧美风情| 91在线高清免费观看| 久久久久免费精品国产| 亚洲黄页视频免费观看| 国产精品久久久久秋霞鲁丝| 国产一区av在线| 久久久久久国产精品三级玉女聊斋| 亚洲精品白浆高清久久久久久| 中文字幕在线看视频国产欧美| 国产精品夜色7777狼人| 久热精品视频在线| 欧美老女人性视频| 欧美日韩国产一区二区| 国产精品丝袜一区二区三区| 欧美中文在线观看国产| 日韩电视剧在线观看免费网站| 亚洲视频一区二区| 国产精品99蜜臀久久不卡二区| 最近2019中文免费高清视频观看www99| 亚洲高清色综合| 91在线免费网站| 色哟哟亚洲精品一区二区| 韩国日本不卡在线| 欧美亚洲一级片| 欧美精品日韩www.p站| 久久精品一区中文字幕| 全色精品综合影院| 久久精品视频在线观看| 欧美噜噜久久久xxx| 亚洲国产美女精品久久久久∴| 欧美大片免费看| 自拍视频国产精品| 57pao成人国产永久免费| 久久这里有精品视频| 国自在线精品视频| 激情懂色av一区av二区av| 中文字幕精品一区二区精品| 午夜美女久久久久爽久久| 正在播放亚洲1区| 久久久久久中文| 久久国产精品久久久久久久久久| 欧美裸体xxxxx| 久久久久久久久国产精品| 久久久女女女女999久久| 欧美最顶级的aⅴ艳星| 精品自拍视频在线观看| 亚洲女人天堂av| 国产午夜精品一区二区三区| 国产精品视频免费观看www| 欧美成人精品三级在线观看| 亚洲伊人一本大道中文字幕| 日韩欧美成人精品| 日韩精品免费在线视频| 亚洲另类激情图| 国产日韩中文字幕在线| 亚洲精品av在线| 国产日韩欧美夫妻视频在线观看| 欧美成人精品在线视频| 亚洲精品视频中文字幕| 欧美多人乱p欧美4p久久| 九九热在线精品视频| 性色av一区二区三区在线观看| 亚洲一区二区三区sesese| 亚洲成人激情小说| 国产精品美女无圣光视频| 国产成人自拍视频在线观看| 97精品视频在线观看| 久久精品99久久久久久久久| 亚洲欧美一区二区精品久久久| 91精品视频免费观看| 亚洲成人av在线| 日韩av成人在线| 久久人人97超碰精品888| 2020国产精品视频| www高清在线视频日韩欧美| 国产精品久久久久久久久久久久久久| 久久网福利资源网站| 狠狠久久五月精品中文字幕| 日本一区二区三区四区视频| 国产一区二区三区中文| 国产精品mp4| 日韩欧美在线视频| 日韩在线观看视频免费|