摘要:最近寫項目需要畫出應用程序調用鏈的網路拓撲圖,完全自己寫需要花費些時間,那么首先想到的是但的自定義寫法寫起來非常麻煩,而且它的文檔都是基于配置說明的,對于自定義開發不太方便,嘗試后果斷放棄,改用,自己完全可控。
最近寫項目需要畫出應用程序調用鏈的網路拓撲圖,完全自己寫需要花費些時間,那么首先想到的是echarts,但echarts的自定義寫法寫起來非常麻煩,而且它的文檔都是基于配置說明的,對于自定義開發不太方便,嘗試后果斷放棄,改用D3.js,自己完全可控。
我們先看看效果
我把代碼分享下,供和我一樣剛接觸D3的同學參考,不對的地方歡迎指正!
完整代碼:
html:
Title
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, // }, ] }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/95832.html
摘要:最近寫項目需要畫出應用程序調用鏈的網路拓撲圖,完全自己寫需要花費些時間,那么首先想到的是但的自定義寫法寫起來非常麻煩,而且它的文檔都是基于配置說明的,對于自定義開發不太方便,嘗試后果斷放棄,改用,自己完全可控。 最近寫項目需要畫出應用程序調用鏈的網路拓撲圖,完全自己寫需要花費些時間,那么首先想到的是echarts,但echarts的自定義寫法寫起來非常麻煩,而且它的文檔都是基于配置說明...
摘要:它的全稱是數據驅動文檔,并且它被稱為一個互動和動態的數據可視化庫網絡。我們將使用文本編輯器和瀏覽器。出于測試目的,建議使用工具來檢查和調試和,例如或。使矩形反映數據目前,我們陣列中的所有矩形沿軸具有相同的位置,并且不代表高度方面的數據。 歡迎大家前往騰訊云+社區,獲取更多騰訊海量技術實踐干貨哦~ 本文由獨木橋先生 發表于云+社區專欄 介紹 D3.js是一個JavaScript庫。它的...
摘要:和之前用的有啥不同和都是用來生成各類圖表的,區別的話可以從使用方法和實現方式上是通過來繪制圖形具體使用方法是通過方法初始化一個實例并通過方法生成一個簡單的柱狀圖很輕松搞定。通過來繪制圖形使用時需要先創建畫布元素然后進行各種繪制圖形。 你的淘寶年度消費報告那炫酷的圖表一下子讓人忘了自己花了多少錢,各大門戶網站、媒體都開始大量使用圖表,于是乎又有一批工具等待我們學習的路上 什么是D3.js...
閱讀 3638·2021-11-25 09:43
閱讀 636·2021-09-22 15:59
閱讀 1744·2021-09-06 15:00
閱讀 1769·2021-09-02 09:54
閱讀 689·2019-08-30 15:56
閱讀 1176·2019-08-29 17:14
閱讀 1839·2019-08-29 13:15
閱讀 880·2019-08-28 18:28