G6
G2
G6
F2
L7
墨者学院
关于 G6
图表示例
API 文档
使用教程
返回旧版
树图
紧凑树
系统树
脑图树
辐射树
树图-边上显示label
文件系统
自定义树图
大数据量的树图
一般图
ER 图
流程图
流程图自定义元素
Force-directed 力导图
Fruchterman 布局参数变化
Dagre 参数变化
Circular 环图参数变化
Radial 布局参数变化
布局数据变化
Radial 交互扩展节点
布局的时机监听
自定义布局 - 二分图
子图布局
其他表达形式
Sankey 桑基图
Arc Diagram 弧线图
Cicular Arc Diagram 环形弧线图
Bubbles 力导气泡
树和气泡
元素
内置边
内置节点
节点-卡片
节点-环形柱状图
节点-面积图
节点-折线图
节点-列表
节点-饼图
节点-标注图
节点-多标签
节点-堆积柱状图
边-贝塞尔曲线
边-内置弧线
边-内置折线
边-自定义折线方法1
边-自定义折线方法2
边-多标签
箭头-内置
箭头-自定义边带有自定义箭头
文本-省略
文本-换行
交互
鼠标事件更新标签
高亮节点
切换节点图片
动态加载数据
动态加载多条数据
Circle节点分组
Rect节点分组
点击扩展节点
动画
状态切换
节点动画
默认动画
自定义动画
辅助工具
Tooltip 节点和边的提示框
Minimap 缩略图
Grid 网格
Edge Bundling 边绑定
Context Menu 右键菜单
响应节点区域事件
点击节点移动到中心
复杂案例
美国航线边绑定
北京地铁
聚类的折叠/扩展交互
自定义资金流转图
大数据量的树图
源码
复制成功
复制失败
全屏
复制
运行
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>大数据量的树图</title> <style>::-webkit-scrollbar{display:none;}html,body{overflow:hidden;margin:0;}</style> </head> <body> <div id="mountNode"></div> <script>/*Fixing iframe window.innerHeight 0 issue in Safari*/document.body.clientHeight;</script> <script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.g6-3.1.1/build/g6.js"></script> <script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.hierarchy-0.5.0/build/hierarchy.js"></script> <script src="https://gw.alipayobjects.com/os/antv/assets/lib/jquery-3.2.1.min.js"></script> <script> var _this = this; var ERROR_COLOR = "#F5222D"; var SIMPLE_TREE_NODE = "simple-tree-node"; var TREE_NODE = "tree-node"; var SOFAROUTER_TEXT_CLASS = "sofarouter-text-class"; var SOFAROUTER_RECT_CLASS = "sofarouter-rect-class"; var CANVAS_WIDTH = window.innerWidth; var CANVAS_HEIGHT = window.innerHeight; var LIMIT_OVERFLOW_WIDTH = CANVAS_WIDTH - 100; var LIMIT_OVERFLOW_HEIGHT = CANVAS_HEIGHT - 100; var TIP_HEIGHT = 28; var getNodeConfig = function getNodeConfig(node) { if (node.nodeError) { return { basicColor: ERROR_COLOR, fontColor: "#FFF", borderColor: ERROR_COLOR, bgColor: "#E66A6C" }; } var config = { basicColor: "#722ED1", fontColor: "#722ED1", borderColor: "#722ED1", bgColor: "#F6EDFC" }; switch (node.type) { case "root": { config = { basicColor: "#E3E6E8", fontColor: "rgba(0,0,0,0.85)", borderColor: "#E3E6E8", bgColor: "#F7F9FA" }; break; } case "httpclient": case "rest": case "mvc": case "rpc": case "rpc2jvm": config = { basicColor: "#2F54EB", fontColor: "#2F54EB", borderColor: "#2F54EB", bgColor: "#F3F6FD" }; break; case "db": config = { basicColor: "#52C41A", fontColor: "#52C41A", borderColor: "#52C41A", bgColor: "#F4FCEB" }; break; case "msgPub": case "msgSub": case "zqmsgSend": case "zqmsgRecv": case "antqPub": case "antqSub": config = { basicColor: "#FA8C16", fontColor: "#FA8C16", borderColor: "#FA8C16", bgColor: "#FCF4E3" }; break; case "zdalTair": case "zdalOcs": case "zdalOss": default: break; } return config; }; var COLLAPSE_ICON = function COLLAPSE_ICON(x, y, r) { return [["M", x - r, y], ["a", r, r, 0, 1, 0, r * 2, 0], ["a", r, r, 0, 1, 0, -r * 2, 0], ["M", x - r + 4, y], ["L", x - r + 2 * r - 4, y]]; }; var EXPAND_ICON = function EXPAND_ICON(x, y, r) { return [["M", x - r, y], ["a", r, r, 0, 1, 0, r * 2, 0], ["a", r, r, 0, 1, 0, -r * 2, 0], ["M", x - r + 4, y], ["L", x - r + 2 * r - 4, y], ["M", x - r + r, y - r + 4], ["L", x, y + r - 4]]; }; /* 精简节点和复杂节点共用的一些方法 */ var nodeBasicMethod = { createNodeBox: function createNodeBox(group, config, width, height, isRoot) { /* 最外面的大矩形 */ var container = group.addShape("rect", { attrs: { x: 0, y: 0, width: width, height: height // fill: '#FFF', // stroke: '#000', } }); if (!isRoot) { /* 左边的小圆点 */ group.addShape("circle", { attrs: { x: 3, y: height / 2, r: 6, fill: config.basicColor } }); } /* 矩形 */ group.addShape("rect", { attrs: { x: 3, y: 0, width: width - 19, height: height, fill: config.bgColor, stroke: config.borderColor, radius: 2, cursor: "pointer" } }); /* 左边的粗线 */ group.addShape("rect", { attrs: { x: 3, y: 0, width: 3, height: height, fill: config.basicColor, radius: 1.5 } }); return container; }, /* 生成树上的 marker */ createNodeMarker: function createNodeMarker(group, collapsed, x, y) { group.addShape("circle", { attrs: { x: x, y: y, r: 13, fill: "rgba(47, 84, 235, 0.05)", opacity: 0, zIndex: -2 }, className: "collapse-icon-bg" }); group.addShape("marker", { attrs: { x: x, y: y, radius: 7, symbol: collapsed ? EXPAND_ICON : COLLAPSE_ICON, stroke: "rgba(0,0,0,0.25)", fill: "rgba(0,0,0,0)", lineWidth: 1, cursor: "pointer" }, className: "collapse-icon" }); }, afterDraw: function afterDraw(cfg, group) { /* 操作 marker 的背景色显示隐藏 */ var icon = group.findByClassName("collapse-icon"); if (icon) { var bg = group.findByClassName("collapse-icon-bg"); icon.on("mouseenter", function() { bg.attr("opacity", 1); graph.get("canvas").draw(); }); icon.on("mouseleave", function() { bg.attr("opacity", 0); graph.get("canvas").draw(); }); } /* ip 显示 */ var ipBox = group.findByClassName("ip-box"); if (ipBox) { /* ip 复制的几个元素 */ var ipLine = group.findByClassName("ip-cp-line"); var ipBG = group.findByClassName("ip-cp-bg"); var ipIcon = group.findByClassName("ip-cp-icon"); var ipCPBox = group.findByClassName("ip-cp-box"); var onMouseEnter = function onMouseEnter() { _this.ipHideTimer && clearTimeout(_this.ipHideTimer); ipLine.attr("opacity", 1); ipBG.attr("opacity", 1); ipIcon.attr("opacity", 1); graph.get("canvas").draw(); }; var onMouseLeave = function onMouseLeave() { _this.ipHideTimer = setTimeout(function() { ipLine.attr("opacity", 0); ipBG.attr("opacity", 0); ipIcon.attr("opacity", 0); graph.get("canvas").draw(); }, 100); }; ipBox.on("mouseenter", function() { onMouseEnter(); }); ipBox.on("mouseleave", function() { onMouseLeave(); }); ipCPBox.on("mouseenter", function() { onMouseEnter(); }); ipCPBox.on("mouseleave", function() { onMouseLeave(); }); ipCPBox.on("click", function() {}); } }, setState: function setState(name, value, item) { var hasOpacityClass = ["ip-cp-line", "ip-cp-bg", "ip-cp-icon", "ip-cp-box", "ip-box", "collapse-icon-bg"]; var group = item.getContainer(); var childrens = group.get("children"); graph.setAutoPaint(false); if (name === "emptiness") { if (value) { childrens.forEach(function(shape) { if (hasOpacityClass.indexOf(shape.get("className")) > -1) { return; } shape.attr("opacity", 0.4); }); } else { childrens.forEach(function(shape) { if (hasOpacityClass.indexOf(shape.get("className")) > -1) { return; } shape.attr("opacity", 1); }); } } graph.setAutoPaint(true); } }; /* 精简节点 */ G6.registerNode(SIMPLE_TREE_NODE, { drawShape: function drawShape(cfg, group) { var config = getNodeConfig(cfg); var isRoot = cfg.type === "root"; var nodeError = cfg.nodeError; var data = cfg; var container = nodeBasicMethod.createNodeBox(group, config, 171, 38, isRoot); /* name */ var nameText = group.addShape("text", { attrs: { text: fittingString(cfg.name, 133, 12), x: 19, y: 19, fontSize: 14, fontWeight: 700, textAlign: "left", textBaseline: "middle", fill: config.fontColor, cursor: "pointer" } }); if (nodeError) { group.addShape("image", { attrs: { x: 119, y: 5, height: 35, width: 35, img: "/static/images/warning-circle.svg" } }); } var hasChildren = cfg.children && cfg.children.length > 0; if (hasChildren) { nodeBasicMethod.createNodeMarker(group, cfg.collapsed, 164, 19); } return container; }, afterDraw: nodeBasicMethod.afterDraw, setState: nodeBasicMethod.setState }, "single-shape"); /* 复杂节点 */ G6.registerNode(TREE_NODE, { drawShape: function drawShape(cfg, group) { var config = getNodeConfig(cfg); var isRoot = cfg.type === "root"; var data = cfg; var nodeError = data.nodeError; /* 最外面的大矩形 */ var container = nodeBasicMethod.createNodeBox(group, config, 243, 64, isRoot); if (data.type !== "root") { /* 上边的 type */ group.addShape("text", { attrs: { text: data.type, x: 3, y: -10, fontSize: 12, textAlign: "left", textBaseline: "middle", fill: "rgba(0,0,0,0.65)" } }); } var ipWidth = 0; if (data.ip) { /* ip start */ /* ipBox */ var ipRect = group.addShape("rect", { attrs: { fill: nodeError ? null : "#FFF", stroke: nodeError ? "rgba(255,255,255,0.65)" : null, radius: 2, cursor: "pointer" } }); /* ip */ var ipText = group.addShape("text", { attrs: { text: data.ip, x: 0, y: 19, fontSize: 12, textAlign: "left", textBaseline: "middle", fill: nodeError ? "rgba(255,255,255,0.85)" : "rgba(0,0,0,0.65)", cursor: "pointer" } }); var ipBBox = ipText.getBBox(); /* ip 的文字总是距离右边 12px */ ipText.attr({ x: 224 - 12 - ipBBox.width }); /* ipBox */ ipRect.attr({ x: 224 - 12 - ipBBox.width - 4, y: ipBBox.minY - 5, width: ipBBox.width + 8, height: ipBBox.height + 10 }); /* 在 IP 元素上面覆盖一层透明层,方便监听 hover 事件 */ group.addShape("rect", { attrs: { stroke: "", cursor: "pointer", x: 224 - 12 - ipBBox.width - 4, y: ipBBox.minY - 5, width: ipBBox.width + 8, height: ipBBox.height + 10, fill: "#fff", opacity: 0 }, className: "ip-box" }); /* copyIpLine */ group.addShape("rect", { attrs: { x: 194, y: 7, width: 1, height: 24, fill: "#E3E6E8", opacity: 0 }, className: "ip-cp-line" }); /* copyIpBG */ group.addShape("rect", { attrs: { x: 195, y: 8, width: 22, height: 22, fill: "#FFF", cursor: "pointer", opacity: 0 }, className: "ip-cp-bg" }); /* copyIpIcon */ group.addShape("image", { attrs: { x: 200, y: 13, height: 12, width: 10, img: "https://os.alipayobjects.com/rmsportal/DFhnQEhHyPjSGYW.png", cursor: "pointer", opacity: 0 }, className: "ip-cp-icon" }); /* 放一个透明的矩形在 icon 区域上,方便监听点击 */ group.addShape("rect", { attrs: { x: 195, y: 8, width: 22, height: 22, fill: "#FFF", cursor: "pointer", opacity: 0 }, className: "ip-cp-box", tooltip: "复制IP" }); var ipRectBBox = ipRect.getBBox(); ipWidth = ipRectBBox.width; /* ip end */ } /* name */ var nameText = group.addShape("text", { attrs: { /* 根据 IP 的长度计算出 剩下的 留给 name 的长度! */ text: fittingString(data.name, 224 - ipWidth - 20, 12), //data.name, x: 19, y: 19, fontSize: 14, fontWeight: 700, textAlign: "left", textBaseline: "middle", fill: config.fontColor, cursor: "pointer" // tooltip: cfg.name, } }); /* 下面的文字 */ var remarkText = group.addShape("text", { attrs: { text: fittingString(data.keyInfo, 204, 12), x: 19, y: 45, fontSize: 14, textAlign: "left", textBaseline: "middle", fill: config.fontColor, cursor: "pointer" // className: 'keyInfo', // tooltip: cfg.keyInfo, } }); if (nodeError) { group.addShape("image", { attrs: { x: 191, y: 32, height: 35, width: 35, img: "/static/images/warning-circle.svg" } }); } var hasChildren = cfg.children && cfg.children.length > 0; if (hasChildren) { nodeBasicMethod.createNodeMarker(group, cfg.collapsed, 236, 32); } return container; }, afterDraw: nodeBasicMethod.afterDraw, setState: nodeBasicMethod.setState }, "single-shape"); /* 是否显示 sofarouter,通过透明度来控制 */ G6.registerEdge("tree-edge", { draw: function draw(cfg, group) { var targetNode = cfg.targetNode.getModel(); var edgeError = !!targetNode.edgeError; var startPoint = cfg.startPoint; var endPoint = cfg.endPoint; var controlPoints = this.getControlPoints(cfg); var points = [startPoint]; // 添加起始点 // 添加控制点 if (controlPoints) { points = points.concat(controlPoints); } // 添加结束点 points.push(endPoint); var path = this.getPath(points); group.addShape("path", { attrs: { path: path, lineWidth: 12, stroke: edgeError ? "rgba(245,34,45,0.05)" : "rgba(47,84,235,0.05)", opacity: 0, zIndex: 0 }, className: "line-bg" }); var keyShape = group.addShape("path", { attrs: { path: path, lineWidth: 1, stroke: edgeError ? "#FF7875" : "rgba(0,0,0,0.25)", zIndex: 1, lineAppendWidth: 12 }, edgeError: !!edgeError }); /* 连接线的中间点 */ var centerPoint = { x: startPoint.x + (endPoint.x - startPoint.x) / 2, y: startPoint.y + (endPoint.y - startPoint.y) / 2 }; var textRect = group.addShape("rect", { attrs: { fill: "#FFF1F0", radius: 2, cursor: "pointer", opacity: 1 }, /* sofarouter 需要 class,以便控制 显示隐藏*/ className: SOFAROUTER_RECT_CLASS }); var text = group.addShape("text", { attrs: { text: "edge-label", x: 0, y: 0, fontSize: 12, textAlign: "left", textBaseline: "middle", fill: "#F5222D", opacity: 1 }, /* sofarouter 需要 class,以便控制 显示隐藏*/ className: SOFAROUTER_TEXT_CLASS }); var textBBox = text.getBBox(); /* text 的位置 */ text.attr({ x: centerPoint.x - textBBox.width / 2, y: centerPoint.y }); /* text 的框框 */ textRect.attr({ x: centerPoint.x - textBBox.width / 2 - 4, y: centerPoint.y - textBBox.height / 2 - 5, width: textBBox.width + 8, height: textBBox.height + 10 }); return keyShape; }, /* 操作 线 的背景色显示隐藏 */ afterDraw: function afterDraw(cfg, group) { /* 背景色 */ var lineBG = group.get("children")[0]; // 顺序根据 draw 时确定 /* 线条 */ var line = group.get("children")[1]; line.on("mouseenter", function() { lineBG.attr("opacity", "1"); /* 线条如果在没有错误的情况下,在 hover 时候,是需要变成蓝色的 */ if (!line.get("edgeError")) { line.attr("stroke", "#2F54EB"); } graph.get("canvas").draw(); }); line.on("mouseleave", function() { lineBG.attr("opacity", "0"); if (!line.get("edgeError")) { line.attr("stroke", "rgba(0,0,0,0.25)"); } graph.get("canvas").draw(); }); }, setState: function setState(name, value, item) { var group = item.getContainer(); var childrens = group.get("children"); graph.setAutoPaint(true); if (name === "emptiness") { if (value) { childrens.forEach(function(shape) { if (shape.get("className") === "line-bg") { return; } shape.attr("opacity", 0.4); }); } else { childrens.forEach(function(shape) { if (shape.get("className") === "line-bg") { return; } shape.attr("opacity", 1); }); } } graph.setAutoPaint(true); }, update: null }, "cubic-horizontal"); G6.registerBehavior("three-finger-drag-canvas", { getEvents: function getEvents() { return { "canvas:dragstart": "onDragStart", "canvas:drag": "onDrag", "canvas:dragend": "onDragEnd" }; }, onDragStart: function onDragStart(ev) { ev.preventDefault(); _this.dragDx = ev.x; _this.dragDy = ev.y; }, onDrag: function onDrag(ev) { ev.preventDefault(); translate(_this.dragDx - ev.x, _this.dragDy - ev.y); }, onDragEnd: function onDragEnd(ev) { ev.preventDefault(); } }); G6.registerBehavior("double-finger-drag-canvas", { getEvents: function getEvents() { return { wheel: "onWheel" }; }, onWheel: function onWheel(ev) { if (ev.ctrlKey) { var canvas = graph.get("canvas"); var pixelRatio = canvas.get("pixelRatio"); var point = canvas.getPointByClient(ev.clientX, ev.clientY); var ratio = graph.getZoom(); if (ev.wheelDelta > 0) { ratio = ratio + ratio * 0.05; } else { ratio = ratio - ratio * 0.05; } graph.zoomTo(ratio, { x: point.x / pixelRatio, y: point.y / pixelRatio }); } else { var x = ev.deltaX || ev.movementX; var y = ev.deltaY || ev.movementY; translate(x, y); } ev.preventDefault(); } }); var selectedItem = void 0; graph = new G6.TreeGraph({ container: "mountNode", width: CANVAS_WIDTH, height: CANVAS_HEIGHT, zoom: 0.2, modes: { default: [{ type: "collapse-expand", shouldUpdate: function shouldUpdate(e) { /* 点击 node 禁止展开收缩 */ if (e.target.get("className") !== "collapse-icon") { return false; } return true; }, onChange: function onChange(item, collapsed) { selectedItem = item; var icon = item.get("group").findByClassName("collapse-icon"); if (collapsed) { icon.attr("symbol", EXPAND_ICON); } else { icon.attr("symbol", COLLAPSE_ICON); } }, animate: { callback: function callback() { debugger; graph.focusItem(selectedItem); } } }, "double-finger-drag-canvas", "three-finger-drag-canvas", { type: "tooltip", formatText: function formatText(data) { return "<div>" + data.name + "</div>"; } }, { type: "drag-canvas", shouldUpdate: function shouldUpdate() { return false; }, shouldEnd: function shouldUpdate() { return false; } }] }, defaultNode: { shape: TREE_NODE, anchorPoints: [[0, 0.5], [1, 0.5]] }, defaultEdge: { shape: "tree-edge", style: { stroke: "#A3B1BF" } }, layout: function layout(data) { var result = Hierarchy.compactBox(data, { direction: "LR", getId: function getId(d) { return d.id; }, getWidth: function getWidth() { return 243; }, getVGap: function getVGap() { return 24; }, getHGap: function getHGap() { return 50; } }); return result; } }); function strLen(str) { var len = 0; // if(!str) { // return len; // } for (var i = 0; i < str.length; i++) { if (str.charCodeAt(i) > 0 && str.charCodeAt(i) < 128) { len++; } else { len += 2; } } return len; } function fittingString(str, maxWidth, fontSize) { var fontWidth = fontSize * 1.3; //字号+边距 maxWidth = maxWidth * 2; // 需要根据自己项目调整 var width = strLen(str) * fontWidth; var ellipsis = '…'; if (width > maxWidth) { var actualLen = Math.floor((maxWidth - 10) / fontWidth); var result = str.substring(0, actualLen) + ellipsis; return result; } return str; }; function translate(x, y) { // graph.translate(-x, -y); var moveX = x; var moveY = y; var containerWidth = graph.get("width"); var containerHeight = graph.get("height"); /* 获得当前偏移量*/ var group = graph.get("group"); var bbox = group.getBBox(); var leftTopPoint = graph.getCanvasByPoint(bbox.minX, bbox.minY); var rightBottomPoint = graph.getCanvasByPoint(bbox.maxX, bbox.maxY); /* 如果 x 轴在区域内,不允许左右超过100 */ if (x < 0 && leftTopPoint.x - x > LIMIT_OVERFLOW_WIDTH) { moveX = 0; } if (x > 0 && rightBottomPoint.x - x < containerWidth - LIMIT_OVERFLOW_WIDTH) { moveX = 0; } if (y < 0 && leftTopPoint.y - y > LIMIT_OVERFLOW_HEIGHT) { moveY = 0; } if (y > 0 && rightBottomPoint.y - y < containerHeight - LIMIT_OVERFLOW_HEIGHT) { moveY = 0; } graph.translate(-moveX, -moveY); }; function fitView1() { var group = graph.get("group"); var width = graph.get("width"); // 视窗的宽度 var height = graph.get("height"); // 视窗的高度 group.resetMatrix(); // 内容 var bbox = group.getBBox(); // 视窗中间 var viewCenter = { x: width / 2, y: height / 2 }; /* 内容的中点 */ var groupCenter = { x: bbox.x + bbox.width / 2, y: bbox.y + bbox.height / 2 }; graph.translate(-bbox.x + 16, viewCenter.y - groupCenter.y); }; function zoomTo(ratio, center) { var width = graph.get("width"); // 视窗的宽度 var height = graph.get("height"); // 视窗的高度 var viewCenter = { x: width / 2, y: height / 2 }; graph.zoomTo(ratio, center || viewCenter); }; function formatData(data) { var recursiveTraverse = function recursiveTraverse(node) { var level = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; var appName = "testappName"; var keyInfo = "testkeyinfo"; var ip = "111.22.33.44"; var targetNode = { id: node.key + "", rpcId: node.rpcId, level: level, type: node.appName === "USER" ? "root" : node.type, name: appName, keyInfo: keyInfo || "-", ip: ip, nodeError: false, edgeError: false, sofarouter: true, sofarouterError: true, asyn: false, origin: node }; if (node.children) { targetNode.children = []; node.children.forEach(function(item) { targetNode.children.push(recursiveTraverse(item, level + 1)); }); } return targetNode; }; var result = recursiveTraverse(data); return result; } graph.on('beforepaint', function() { var topLeft = graph.getPointByCanvas(0, 0); var bottomRight = graph.getPointByCanvas(1000, 600); graph.getNodes().forEach(function(node) { var model = node.getModel(); if (model.x < topLeft.x - 200 || model.x > bottomRight.x || model.y < topLeft.y || model.y > bottomRight.y) { node.getContainer().hide(); } else { node.getContainer().show(); } }); var edges = graph.getEdges(); edges.forEach(function(edge) { var sourceNode = edge.get('sourceNode'); var targetNode = edge.get('targetNode'); if (!sourceNode.get('visible') && !targetNode.get('visible')) { edge.hide(); } else { edge.show(); } }); }); $.getJSON('https://gw.alipayobjects.com/os/basement_prod/dd089904-664c-4699-9593-e0df409558c0.json', function(data) { data = formatData(data); graph.data(data); graph.render(); graph.fitView(); graph.zoom(0.3); }); </script></body> </html>