国产99久久精品_欧美日本韩国一区二区_激情小说综合网_欧美一级二级视频_午夜av电影_日本久久精品视频

最新文章專題視頻專題問答1問答10問答100問答1000問答2000關鍵字專題1關鍵字專題50關鍵字專題500關鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關鍵字專題關鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
當前位置: 首頁 - 科技 - 知識百科 - 正文

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

來源:懂視網 責編:小采 時間:2020-11-27 22:12:10
文檔

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

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

最近寫項目需要畫出應用程序調用鏈的網路拓撲圖,完全自己寫需要花費些時間,那么首先想到的是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,
 // },
 ]
}

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

文檔

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

D3.js實現拓撲圖的示例代碼:最近寫項目需要畫出應用程序調用鏈的網路拓撲圖,完全自己寫需要花費些時間,那么首先想到的是echarts,但echarts的自定義寫法寫起來非常麻煩,而且它的文檔都是基于配置說明的,對于自定義開發不太方便,嘗試后果斷放棄,改用D3.js,自己完全可控。 我們先看
推薦度:
標簽: 實現 代碼 d3
  • 熱門焦點

最新推薦

猜你喜歡

熱門推薦

專題
Top
主站蜘蛛池模板: 看全色黄大色黄女片爽毛片 | 欧美日韩一区二区三区在线观看 | 国产成人一区二区三区视频免费蜜 | 国产欧美一区二区精品性色99 | 免费观看国产精品 | 国产欧美一区二区三区鸳鸯浴 | 欧美 日韩 国产 在线 | 国产精品日本一区二区不卡视频 | 精品伊人久久大线蕉色首页 | 国产亚洲精品一品区99热 | 国产成人免费视频精品一区二区 | 国产精品久久久久久一级毛片 | 日本免费一区尤物 | 91精品一区二区三区久久久久 | 综合欧美亚洲 | 日韩专区在线观看 | 国产精品免费看久久久麻豆 | 自拍偷拍欧美亚洲 | 欧美 日韩 国产 在线 | 欧美网站在线观看 | 中文字幕日韩一区二区三区不卡 | 国产精品香蕉在线观看 | 欧美日韩一区二区三区在线播放 | 另类专区另类专区亚洲 | 能看毛片的网站 | 国产高清在线免费 | 一区二区日韩 | 国产日本在线 | 欧美人在线一区二区三区 | 欧美日韩精品一区二区三区视频在线 | 国产一区二区三区久久精品 | 亚洲国产精品欧美日韩一区二区 | 免费一级淫片 | 天天伊人| 欧美日韩在线第一页 | 五月天婷婷久久 | 九九精品成人免费国产片 | 国产精品hd免费观看 | 一区二区久久 | 欧美日韩精| 精品久久中文网址 |