這篇文章主要介紹了關於如何用D3.js實現拓撲圖,有著一定的參考價值,現在分享給大家,有需要的朋友可以參考一下
最近寫專案需要畫出應用程式呼叫鏈的網路拓撲圖,完全自己寫需要花費一些時間,那麼首先想到的是echarts,但echarts的自訂寫法寫起來非常麻煩,而且它的文檔都是基於配置說明的,對於自定義開發不太方便,嘗試後果斷放棄,改用D3.js,自己完全可控。
我們先看看效果
我把程式碼分享下,供和我一樣剛接觸D3的同學參考,不對的地方歡迎指正!
完整程式碼:
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('#togo',__options); t.render(); </script>
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 + ")") } }
資料:
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中文網!
相關推薦:
#以上是如何用D3.js實現拓樸圖的詳細內容。更多資訊請關注PHP中文網其他相關文章!

Python和JavaScript的未來趨勢包括:1.Python將鞏固在科學計算和AI領域的地位,2.JavaScript將推動Web技術發展,3.跨平台開發將成為熱門,4.性能優化將是重點。兩者都將繼續在各自領域擴展應用場景,並在性能上有更多突破。

Python和JavaScript在開發環境上的選擇都很重要。 1)Python的開發環境包括PyCharm、JupyterNotebook和Anaconda,適合數據科學和快速原型開發。 2)JavaScript的開發環境包括Node.js、VSCode和Webpack,適用於前端和後端開發。根據項目需求選擇合適的工具可以提高開發效率和項目成功率。

是的,JavaScript的引擎核心是用C語言編寫的。 1)C語言提供了高效性能和底層控制,適合JavaScript引擎的開發。 2)以V8引擎為例,其核心用C 編寫,結合了C的效率和麵向對象特性。 3)JavaScript引擎的工作原理包括解析、編譯和執行,C語言在這些過程中發揮關鍵作用。

JavaScript是現代網站的核心,因為它增強了網頁的交互性和動態性。 1)它允許在不刷新頁面的情況下改變內容,2)通過DOMAPI操作網頁,3)支持複雜的交互效果如動畫和拖放,4)優化性能和最佳實踐提高用戶體驗。

C 和JavaScript通過WebAssembly實現互操作性。 1)C 代碼編譯成WebAssembly模塊,引入到JavaScript環境中,增強計算能力。 2)在遊戲開發中,C 處理物理引擎和圖形渲染,JavaScript負責遊戲邏輯和用戶界面。

JavaScript在網站、移動應用、桌面應用和服務器端編程中均有廣泛應用。 1)在網站開發中,JavaScript與HTML、CSS一起操作DOM,實現動態效果,並支持如jQuery、React等框架。 2)通過ReactNative和Ionic,JavaScript用於開發跨平台移動應用。 3)Electron框架使JavaScript能構建桌面應用。 4)Node.js讓JavaScript在服務器端運行,支持高並發請求。

Python更適合數據科學和自動化,JavaScript更適合前端和全棧開發。 1.Python在數據科學和機器學習中表現出色,使用NumPy、Pandas等庫進行數據處理和建模。 2.Python在自動化和腳本編寫方面簡潔高效。 3.JavaScript在前端開發中不可或缺,用於構建動態網頁和單頁面應用。 4.JavaScript通過Node.js在後端開發中發揮作用,支持全棧開發。

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。1)C 用于解析JavaScript源码并生成抽象语法树。2)C 负责生成和执行字节码。3)C 实现JIT编译器,在运行时优化和编译热点代码,显著提高JavaScript的执行效率。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

Dreamweaver Mac版
視覺化網頁開發工具

SecLists
SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

SublimeText3 英文版
推薦:為Win版本,支援程式碼提示!

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器