原文链接:https://www.jianshu.com/p/d768505cab78 在此基础上加了点东西

实现了关系图的移动、拖拽、放大功能。以及数据的分类等等。代码稍显繁琐,另外案例中的d3.js为3.5.17版本,请注意安装。


<!DOCTYPE html>
<html>
//                         _ooOoo_
//                        o8888888o
//                        88" . "88
//                        (| -_- |)
//                         O\ = /O
//                     ____/`---'\____
//                   .   ' \\| |// `.
//                    / \\||| : |||// \
//                  / _||||| -:- |||||- \
//                    | | \\\ - /// | |
//                  | \_| ''\---/'' | |
//                   \ .-\__ `-` ___/-. /
//                ___`. .' /--.--\ `. . __
//             ."" '< `.___\_<|>_/___.' >'"".
//            | | : `- \`.;`\ _ /`;.`/ - ` : | |
//              \ \ `-. \_ __\ /__ _/ .-` / /
//      ======`-.____`-.___\_____/___.-`____.-'======
//                         `=---='
//
//      .............................................
//               佛祖保佑             永无BUG
//       佛曰:
//               写字楼里写字间,写字间里程序员;
//               程序人员写程序,又拿程序换酒钱。
//               酒醒只在网上坐,酒醉还来网下眠;
//               酒醉酒醒日复日,网上网下年复年。
//               但愿老死电脑间,不愿鞠躬老板前;
//               奔驰宝马贵者趣,公交自行程序员。
//               别人笑我忒疯癫,我笑自己命太贱;
//               不见满街漂亮妹,哪个归得程序员? <head><style>#divid {/*会出现页面阴影*//*cursor: pointer;*/}.tooltip {font-family: SimHei;font-size: 12px;position: fixed;background: #fff;color: #000;padding: 5px 10px;border-radius: 5px;}</style><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><script src="https://code.jquery.com/jquery-3.4.1.min.js"></script><script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head><body><!-- 力导向图div --><div id="divid"></div><script>$(document).ready(function () {showChart();})function showChart() {var data = {"links": [{"source": "陆洋",//边的源节点// "data": {//     "rzs": "1000万美元"// },"target": "建银国际产业基金管理有限公司",//边的目标节点"relation": "投资1",//线上名称"sourceImg": "",//源节点图片"targetImg": "",//目标节点图片"sourceColor": "#F4793B",//源节点颜色"targetColor": "#0084ff",//目标节点颜色"sourceRadius": "30",//源节点大小"targetRadius": "35",//目标节点大小},{"source": "陆洋","data": {"rzs": "12万美元"},"target": "乾行文化投资公司","relation": "投资","sourceImg": "http://mail.tom.com/error/i2.gif","targetImg": "","sourceColor": "#F4793B","targetColor": "#0084ff","sourceRadius": "30","targetRadius": "35",},{"source": "汪红辉","target": "乾行文化投资公司","relation": "总经理","sourceImg": "","targetImg": "","sourceColor": "#F4793B","targetColor": "#0084ff","sourceRadius": "30","targetRadius": "35",},{"source": "汪红辉","target": "天津裕丰股权投资管理有限公司","relation": "董事","sourceImg": "","sourceImg": "","targetImg": "","sourceColor": "#F4793B","targetColor": "#0084ff","sourceRadius": "30","targetRadius": "35",},{"source": "汪红辉","target": "乾行文化投资公司","relation": "法人","sourceImg": "","targetImg": "","sourceColor": "#F4793B","targetColor": "","sourceRadius": "30","targetRadius": "35",},{"source": "汪红辉","target": "乾行文化投资公司","relation": "董事长","sourceImg": "","targetImg": "","sourceColor": "#F4793B","targetColor": "","sourceRadius": "30","targetRadius": "35",},{"source": "汪红辉","target": "建银国际产业基金管理有限公司","relation": "董事","sourceImg": "","targetImg": "","sourceColor": "#F4793B","targetColor": "#0084ff","sourceRadius": "30","targetRadius": "35",},{"source": "胡章宏","target": "建银国际产业基金管理有限公司","relation": "董事长","sourceImg": "","targetImg": "","sourceColor": "#F4793B","targetColor": "#0084ff","sourceRadius": "30","targetRadius": "35",},]};var options = {};options.backgroundColor = "#eee";options.nodesFontType = "SimHei";options.nodesFontSize = 14;options.lineFontType = "SimHei";options.lineFontSize = 12;options.lineColor = "#000000";options.showExamples = true;options.examplesX = 20;options.examplesY = 450;options.examplesFontColor = "#000000";drawChart("divid", options, data);}function drawChart(divid, options, datas, dataFilter) {var backgroundColor = options.backgroundColor; //背景颜色var nodesFontType = options.nodesFontType; //节点字体var nodesFontSize = options.nodesFontSize; //节点字号var lineFontType = options.lineFontType; //关系字体var lineFontSize = options.lineFontSize; //关系字号var lineColor = options.lineColor; //连线颜色var examplesFontColor = options.examplesFontColor; //关系示例字体颜色var width = window.innerWidth; //画布宽var height = window.innerHeight; //画布高var svgChart = d3.select("svg");svgChart.remove();var tip = $(".tooltip");if (tip.length > 0) {tip.remove();}var sourceDatas = {};sourceDatas.links = [];for (var i = 0; i < datas.links.length; i++) {var jsonObj = {};jsonObj.source = datas.links[i].source;jsonObj.target = datas.links[i].target;jsonObj.relation = datas.links[i].relation;jsonObj.sourceImg = datas.links[i].sourceImg;jsonObj.targetImg = datas.links[i].targetImg;jsonObj.sourceColor = datas.links[i].sourceColor;jsonObj.targetColor = datas.links[i].targetColor;jsonObj.sourceRadius = datas.links[i].sourceRadius;jsonObj.targetRadius = datas.links[i].targetRadius;// 根据关系类型添加连接线的颜色// jsonObj.lineColor = datas.links[i].lineColor;switch (datas.links[i].relation) {case '投资':jsonObj.lineColor = '#458B00';break;case '总经理':jsonObj.lineColor = '#EEEE00';break;case '董事':jsonObj.lineColor = '#8fd2e1';break;case '法人':jsonObj.lineColor = '#c2de96';break;case '董事长':jsonObj.lineColor = '#ff4c00';break;default:jsonObj.lineColor = '#000';}jsonObj.data = datas.links[i].data;sourceDatas.links.push(jsonObj);}var resourceLinks = sourceDatas.links;if (dataFilter != undefined && dataFilter.length > 0) {var indexArray = [];for (var i = 0; i < dataFilter.length; i++) {for (var j = 0; j < resourceLinks.length; j++) {if (resourceLinks[j].relation == dataFilter[i].relation && dataFilter[i].isShow == "false") {indexArray.push(j);}}}if (indexArray.length > 0) {var tempArray = [];for (var j = 0; j < resourceLinks.length; j++) {for (var i = 0; i < indexArray.length; i++) {if (indexArray[i] != j) {if (i == indexArray.length - 1) {tempArray.push(resourceLinks[j]);break;}continue;} else {break;}}}resourceLinks = tempArray;}}var links = resourceLinks;//关系分组  var linkGroup = {};//对连接线进行统计和分组,不区分连接线的方向,只要属于同两个实体,即认为是同一组  var linkmap = {};for (var i = 0; i < links.length; i++) {var key = links[i].source < links[i].target ? links[i].source + ':' + links[i].target : links[i].target + ':' + links[i].source;if (!linkmap.hasOwnProperty(key)) {linkmap[key] = 0;}linkmap[key] += 1;if (!linkGroup.hasOwnProperty(key)) {linkGroup[key] = [];}linkGroup[key].push(links[i]);}//为每一条连接线分配size属性,同时对每一组连接线进行编号  for (var i = 0; i < links.length; i++) {var key = links[i].source < links[i].target ? links[i].source + ':' + links[i].target : links[i].target + ':' + links[i].source;links[i].size = linkmap[key];//同一组的关系进行编号  var group = linkGroup[key];//给节点分配编号  setLinkNumber(group);}//节点var nodes = {};//关系对应颜色var relationColor = {};for (var i = 0; i < links.length; i++) {links[i].source = nodes[links[i].source] || (nodes[links[i].source] = {name: links[i].source,color: links[i].sourceColor,image: links[i].sourceImg,radius: links[i].sourceRadius});links[i].target = nodes[links[i].target] || (nodes[links[i].target] = {name: links[i].target,color: links[i].targetColor,image: links[i].targetImg,radius: links[i].targetRadius});}var sourceData = datas.links;for (var i = 0; i < sourceData.length; i++) {relationColor[sourceData[i].relation] = {"relation": sourceData[i].relation,"lineColor": sourceData[i].lineColor};}nodes = d3.values(nodes);relationColor = d3.values(relationColor);var examples_x = parseFloat(options.examplesX); //关系示例坐标xvar examples_y = parseFloat(options.examplesY); //关系示例坐标yvar examplesLength = 80;var examplesSize = Math.floor((width - examples_x) / examplesLength);var examplesRow = relationColor.length % examplesSize == 0 ? relationColor.length / examplesSize : Math.ceil(relationColor.length / examplesSize);//计算关系示列位置for (var i = 1; i <= relationColor.length; i++) {var num = i % examplesSize == 0 ? examplesSize : i % examplesSize;relationColor[i - 1].x = examples_x + (num - 1) * examplesLength;relationColor[i - 1].y = examples_y + 20 * Math.ceil(i / examplesSize);}if (dataFilter == undefined) {dataFilter = [];for (var i = 0; i < relationColor.length; i++) {dataFilter.push({"relation": relationColor[i].relation,"isShow": "true"});}}//绑定相连节点for (var i = 0; i < nodes.length; i++) {for (var j = 0; j < links.length; j++) {if (nodes[i].name == links[j].source.name) {nodes[i][links[j].target.name] = {name: links[j].target.name};}if (nodes[i].name == links[j].target.name) {nodes[i][links[j].source.name] = {name: links[j].source.name};}}}//D3力导向布局var force = d3.layout.force().nodes(nodes).links(links).size([width, height]).linkDistance(200).charge(-1500).start();// 全图缩放器var zoom = d3.behavior.zoom().scaleExtent([0.25, 2]).on('zoom', zoomFn);var svg = d3.select("#" + divid).append("svg").attr("width", width).attr("height", height).attr("style", "background-color:" + backgroundColor).call(zoom).on('dblclick.zoom', null);// 缩放层(位置必须在 container 之前)var zoomOverlay = svg.append('rect').attr('width', width).attr('height', height).style('fill', 'none').style('pointer-events', 'all');var container = svg.append('g').attr('transform', 'scale(' + 0.6 + ')').attr('class', 'container');// 根据分类进行筛选if (options.showExamples) {var examples = svg.selectAll(".examples").data(relationColor).enter().append("svg:g").attr("fill-opacity", function (d) {for (var i = 0; i < dataFilter.length; i++) {if (d.relation == dataFilter[i].relation && dataFilter[i].isShow == "false") {return 0.2;}}return 1;}).on("click", function (d) {for (var i = 0; i < dataFilter.length; i++) {if (dataFilter[i].relation == d.relation) {if (dataFilter[i].isShow == "true") {dataFilter[i].isShow = "false";} else {dataFilter[i].isShow = "true";}}}drawChart(divid, options, datas, dataFilter);});examples.append("svg:path").attr("d", function (d) {var x1 = d.x;var y1 = d.y;var x2 = x1 + 20;var y2 = y1;return 'M' + x1 + ' ' + y1 + ' L ' + x2 + ' ' + y2;}).style("stroke", function (d) {if (d.lineColor == "") {return lineColor;} else {return d.lineColor;}}).style("stroke-width", 2.5);examples.append("svg:text").style("font-size", "14px").style("fill", examplesFontColor).attr("x", function (d) {if (d.relation.length > 3) {return d.x + 20 + 14 * 4 / 2;}return d.x + 20 + 14 * d.relation.length / 2;}).attr("y", function (d) {return d.y + 5;}).attr('text-anchor', "middle").text(function (d) {if (d.relation.length > 3) {return d.relation.substring(0, 3) + "...";}return d.relation;}).on("mouseover", function (d) {console.log('放到分类上');tooltip.html("<span>" + d.relation + "</span>").style("left", (d3.event.pageX) + "px").style("top", (d3.event.pageY + 20) + "px").style("display", "block").style("position", "absolute").style("opacity", 1.0);}).on("mouseout", function (d, i) {tooltip.style("opacity", 0.0);});}function zoomFn() {console.log('开始移动了');const {translate,scale} = d3.event;console.log(container);container.attr('transform', 'translate(' + translate + ')scale(' + scale * 0.6 + ')');}//设置连接线             var edges_path = container.selectAll(".edgepath").data(links).enter().append("path").attr("marker-end", function (d, i) {var arrowMarker = container.append("marker").attr("id", "arrow" + i).attr("markerUnits", "userSpaceOnUse").attr("markerWidth", "16").attr("markerHeight", "15").attr("viewBox", "0 0 12 12").attr("refX", 9).attr("refY", 6).attr("orient", "auto").append("svg:path").attr("d", "M2,2 L10,6 L2,10 L6,6 L2,2").attr("fill", function () {return d.lineColor = "" ? lineColor : d.lineColor;});return "url(#arrow" + i + ")";}).style("stroke", function (d) {if (d.lineColor == "") {return lineColor;} else {return d.lineColor;}}).style("stroke-width", 1.5).on("mouseover", function (d) {console.log('放到连接线');// 设置参股或是融资信息if (d.data) {tooltip.html("<span>" + '融资额:' + d.data.rzs + "</span>").style("left", (d3.event.pageX) + "px").style("top", (d3.event.pageY + 20) + "px").style("display", "block").style("opacity", 1.0);}//影藏其它连线上文字edges_text.style("fill-opacity", function (edge) {if (edge === d) {return 1;}return 0;})edges_path.style("stroke-width", function (edge) {if (edge === d) {return 4;}return 1.5;})}).on("mouseout", function (d, i) {//显示连线上的文字edges_text.style("fill-opacity", 1);edges_path.style("stroke-width", 1.5);// 隐藏提示信息tooltip.style("opacity", 0.0);});//边上的文字(人物之间的关系),连接线            var edges_text = container.selectAll(".linetext").data(links).enter().append("svg:g").attr("class", "linetext").attr("fill-opacity", 1);edges_text.append("svg:text").style("font-size", (12 + "px")).style("font-family", lineFontType).style("fill", "#000000").attr("y", ".33em").attr('text-anchor', "middle").text(function (d) {return d.relation;});edges_text.insert('rect', 'text').attr('width', function (d) {return d.relation.length * lineFontSize;}).attr('height', function (d) {return lineFontSize;}).attr("y", "-.6em").attr('x', function (d) {return -d.relation.length * lineFontSize / 2;}).style('fill', 'none');// 节点设置,包含圆形图片节点(人物头像)var circle = container.selectAll("circle").data(nodes).enter().append("circle").style("stroke", function (d) {if (d.color == "") {return "#EE8262";} else if (d.color == '#0084ff') {return '#0077c6'} else if (d.color == '#F4793B') {return '#FC3620'}return d.color;}).style("stroke-width", "2px").attr("r", function (d) {return d.radius;}).attr("fill", function (d, i) {//节点图片不为空是添加背景色if (d.image == "") {if (d.color == "") {return "#EE8262";}return d.color;} else {//创建圆形图片var defs = container.append("defs").attr("id", "imgdefs")var catpattern = defs.append("pattern").attr("id", "catpattern" + i).attr("height", 1).attr("width", 1)catpattern.append("image")/* .attr("x", - (img_w / 2 - radius)).attr("y", - (img_h / 2 - radius)) */.attr("width", d.radius * 2).attr("height", d.radius * 2).attr("xlink:href", d.image)return "url(#catpattern" + i + ")";}}).on("mouseover", function (d, i) {console.log('放到人物头像');//影藏其它连线上文字edges_text.style("fill-opacity", function (edge) {if (edge.source === d || edge.target === d) {return 1;}if (edge.source !== d && edge.target !== d) {return 0;}})//其它节点亮度调低circle.style("opacity", function (edge) {var v = d.name;if (edge.name == v || (edge[v] != undefined && edge[v].name == v)) {return 1;} else {return 0.2;}})//其他连先亮度调低edges_path.style("opacity", function (edge) {if (edge.source === d || edge.target === d) {return 1;}if (edge.source !== d && edge.target !== d) {return 0.2;}})//其他节点文字亮度调低nodes_text.style("opacity", function (edge) {var v = d.name;if (edge.name == v || (edge[v] != undefined && edge[v].name == v)) {return 1;} else {return 0.2;}})}).on("mouseout", function (d, i) {//显示连线上的文字edges_text.style("fill-opacity", 1);edges_path.style("opacity", 1);circle.style("opacity", 1);nodes_text.style("opacity", 1);tooltip.style("opacity", 0.0);}).call(force.drag);var tooltip = d3.select("body").append("div").attr("class", "tooltip").attr("opacity", 0.0);// 节点文字设置var nodes_text = container.selectAll(".nodetext").data(nodes).enter().append("text").style("font-size", (nodesFontSize + "px")).style("fill", "#ffffff").style("font-family", nodesFontType).attr('x', function (d) {var name = d.name;//如果小于四个字符,不换行if (name.length < 4) {d3.select(this).append('tspan').attr("dx", -nodesFontSize * (name.length / 2)).attr("dy", 5).text(function () {return name;});} else if (name.length >= 4 && name.length <= 6) {var top = d.name.substring(0, 3);var bot = d.name.substring(3, name.length);d3.select(this).append('tspan').attr("dx", -nodesFontSize * 1.5).attr("dy", -nodesFontSize * 0.5).text(function () {return top;});d3.select(this).append('tspan').attr("dx", -(nodesFontSize * name.length / 2)).attr("dy", nodesFontSize).text(function () {return bot;});} else if (name.length > 7) {var top = d.name.substring(0, 3);var mid = d.name.substring(3, 6);var bot = d.name.substring(6, name.length);d3.select(this).append('tspan').attr("dx", -nodesFontSize * 1.5).attr("dy", -nodesFontSize * 0.5).text(function () {return top;});d3.select(this).append('tspan').attr("dx", -nodesFontSize * 3).attr("dy", nodesFontSize).text(function () {return mid;});d3.select(this).append('tspan').attr("dx", -nodesFontSize * 2).attr("dy", nodesFontSize).text(function () {return "...";});}}).on("mouseover", function (d, i) {console.log('放到关系文字');//影藏其它连线上文字edges_text.style("fill-opacity", function (edge) {if (edge.source === d || edge.target === d) {return 1;}if (edge.source !== d && edge.target !== d) {return 0;}})//其他节点亮度调低circle.style("opacity", function (edge) {var v = d.name;if (edge.name == v || (edge[v] != undefined && edge[v].name == v)) {return 1;} else {return 0.2;}})//其他连线亮度调低edges_path.style("opacity", function (edge) {if (edge.source === d || edge.target === d) {return 1;}if (edge.source !== d && edge.target !== d) {return 0.2;}})//其他节点文字亮度调低nodes_text.style("opacity", function (edge) {var v = d.name;if (edge.name == v || (edge[v] != undefined && edge[v].name == v)) {return 1;} else {return 0.2;}})tooltip.html("<span>" + d.name + "</span>").style("left", (d3.event.pageX) + "px").style("top", (d3.event.pageY + 20) + "px").style("display", "block").style("opacity", 1.0);}).on("mouseout", function (d, i) {//显示连线上的文字edges_text.style("fill-opacity", 1);edges_path.style("opacity", 1);circle.style("opacity", 1);nodes_text.style("opacity", 1);tooltip.style("opacity", 0.0);}).call(force.drag);//   拖动节点var drag = force.drag().on("dragstart", function (d, i) {d.fixed = true; //拖拽开始后设定被拖拽对象为固定d3.event.sourceEvent.stopPropagation();}).on("dragend", function (d, i) {}).on("drag", function (d, i) {});//力学图运动开始时force.on("start", function () {});//力学图运动结束时force.on("end", function () {});force.on("tick", function () {edges_path.attr("d", function (d) {var tan = Math.abs((d.target.y - d.source.y) / (d.target.x - d.source.x)); //圆心连线tan值var x1 = d.target.x - d.source.x > 0 ? Math.sqrt(d.sourceRadius * d.sourceRadius / (tan * tan + 1)) + d.source.x :d.source.x - Math.sqrt(d.sourceRadius * d.sourceRadius / (tan * tan +1)); //起点x坐标var y1 = d.target.y - d.source.y > 0 ? Math.sqrt(d.sourceRadius * d.sourceRadius *tan * tan / (tan * tan + 1)) + d.source.y :d.source.y - Math.sqrt(d.sourceRadius * d.sourceRadius * tan * tan / (tan *tan + 1)); //起点y坐标var x2 = d.target.x - d.source.x > 0 ? d.target.x - Math.sqrt(d.targetRadius * d.targetRadius / (1 + tan * tan)) :d.target.x + Math.sqrt(d.targetRadius * d.targetRadius / (1 + tan *tan)); //终点x坐标var y2 = d.target.y - d.source.y > 0 ? d.target.y - Math.sqrt(d.targetRadius * d.targetRadius * tan * tan / (1 + tan * tan)) :d.target.y + Math.sqrt(d.targetRadius * d.targetRadius * tan * tan / (1 + tan *tan)); //终点y坐标if (d.target.x - d.source.x == 0 || tan == 0) { //斜率无穷大的情况或为0时y1 = d.target.y - d.source.y > 0 ? d.source.y + d.sourceRadius : d.source.y - d.sourceRadius;y2 = d.target.y - d.source.y > 0 ? d.target.y - d.targetRadius : d.target.y + d.targetRadius;}// 防报错if (!x1 || !y1 || !x2 || !y2) {return}if (d.linknum == 0) { //设置编号为0的连接线为直线,其他连接线会均分在两边  d.x_start = x1;d.y_start = y1;d.x_end = x2;d.y_end = y2;return 'M' + x1 + ' ' + y1 + ' L ' + x2 + ' ' + y2;}var a = d.sourceRadius > d.targetRadius ? d.targetRadius * d.linknum / 3 : d.sourceRadius * d.linknum / 3;var xm = d.target.x - d.source.x > 0 ? d.source.x + Math.sqrt((d.sourceRadius * d.sourceRadius - a * a) / (1 + tan * tan)) :d.source.x - Math.sqrt((d.sourceRadius * d.sourceRadius - a * a) / (1 + tan *tan));var ym = d.target.y - d.source.y > 0 ? d.source.y + Math.sqrt((d.sourceRadius * d.sourceRadius - a * a) * tan * tan / (1 + tan * tan)) :d.source.y - Math.sqrt((d.sourceRadius * d.sourceRadius - a * a) * tan * tan / (1 + tan * tan));var xn = d.target.x - d.source.x > 0 ? d.target.x - Math.sqrt((d.targetRadius * d.targetRadius - a * a) / (1 + tan * tan)) :d.target.x + Math.sqrt((d.targetRadius * d.targetRadius - a * a) / (1 + tan *tan));var yn = d.target.y - d.source.y > 0 ? d.target.y - Math.sqrt((d.targetRadius * d.targetRadius - a * a) * tan * tan / (1 + tan * tan)) :d.target.y + Math.sqrt((d.targetRadius * d.targetRadius - a * a) * tan * tan / (1 + tan * tan));if (d.target.x - d.source.x == 0 || tan == 0) { //斜率无穷大或为0时ym = d.target.y - d.source.y > 0 ? d.source.y + Math.sqrt(d.sourceRadius * d.sourceRadius - a * a) : d.source.y - Math.sqrt(d.sourceRadius * d.sourceRadius - a * a);yn = d.target.y - d.source.y > 0 ? d.target.y - Math.sqrt(d.targetRadius * d.targetRadius - a * a) : d.target.y + Math.sqrt(d.targetRadius * d.targetRadius - a * a);}var k = (x1 - x2) / (y2 - y1); //连线垂线的斜率var dx = Math.sqrt(a * a / (1 + k * k)); //相对垂点x轴距离var dy = Math.sqrt(a * a * k * k / (1 + k * k)); //相对垂点y轴距离if ((y2 - y1) == 0) {dx = 0;dy = Math.sqrt(a * a);}if (a > 0) {var xs = k > 0 ? xm - dx : xm + dx;var ys = ym - dy;var xt = k > 0 ? xn - dx : xn + dx;var yt = yn - dy;} else {var xs = k > 0 ? xm + dx : xm - dx;var ys = ym + dy;var xt = k > 0 ? xn + dx : xn - dx;var yt = yn + dy;}//记录连线起始和终止坐标,用于定位线上文字d.x_start = xs;d.y_start = ys;d.x_end = xt;d.y_end = yt;return 'M' + xs + ' ' + ys + ' L ' + xt + ' ' + yt;});//更新连接线上文字的位置edges_text.attr("transform", function (d) {// 防止报错if (!d.x_start || !d.y_start || !d.x_end || !d.y_end) {return}return "translate(" + (d.x_start + d.x_end) / 2 + "," + ((+d.y_start) + (+d.y_end)) / 2 +")" + " rotate(" + Math.atan((d.y_end - d.y_start) / (d.x_end - d.x_start)) *180 / Math.PI + ")";});//更新结点图片和文字circle.attr("cx", function (d) {return d.x});circle.attr("cy", function (d) {return d.y});nodes_text.attr("x", function (d) {return d.x});nodes_text.attr("y", function (d) {return d.y});});}// 分配編號function setLinkNumber(group) {if (group.length == 0) return;if (group.length == 1) {group[0].linknum = 0;return;}var maxLinkNumber = group.length % 2 == 0 ? group.length / 2 : (group.length - 1) / 2;if (group.length % 2 == 0) {var startLinkNum = -maxLinkNumber + 0.5;for (var i = 0; i < group.length; i++) {group[i].linknum = startLinkNum++;}} else {var startLinkNum = -maxLinkNumber;for (var i = 0; i < group.length; i++) {group[i].linknum = startLinkNum++;}}}</script></html>

当然数据是可以从后台拿,不必写死的:

在SpringBoot中动态页面是放在templates文件夹,需要引入thymeleaf

首先,pom.xml中导入thymeleaf的依赖:

<!--引入thymeleaf的依赖-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

在资源配置文件中加入thymeleaf相关配置:

##############################################
#
# thymeleaf静态资源配置
#
##############################################
# 默认路径
spring.thymeleaf.prefix=classpath:/templates/
# 后缀
spring.thymeleaf.suffix=.html
# 模板格式
spring.thymeleaf.mode=HTML5
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.content-type=text/html
spring.thymeleaf.cache=false

这个连接是关于static和templates文件夹的说明,有兴趣可以看看
springBoot项目中的static和templates文件夹
这个连接是关于SpringBoot整合thymeleaf的说明,有兴趣可以看看
SpringBoot整合thymeleaf

好了,废话不多说,下面这张图的数据就是我数据从后台拿的(这个demo是SpringBoot的):

百度网盘demo下载地址:

链接:https://pan.baidu.com/s/1lqtdpOLdMXXL6Bz_Q8F5aA
提取码:rbbp

其它下载地址:https://download.csdn.net/download/weixin_43085797/12369293

D3.js实现人物关系图谱有移动、拖拽、放大功能相关推荐

  1. PersonGraphDataSet近十万的开放人物关系图谱项目

    PersonGraphDataSet PersonGraphDataSet, nearly 10 thousand person2person relationship facts that buil ...

  2. Python + Neo4j(安装)可视化分析漫威十年人物关系图谱

    目录 1 数据爬取 1.1 漫威人物关系图谱网站 1.2 爬取人物关系数据 2 Neo4j的安装及服务启动 2.1 Neo4j下载安装 2.2 开启Neo4j服务 3 数据准备 3.1 加入列名 3. ...

  3. BERT辅助金融领域人物关系图谱构建

    摘要 现有的人员简历信息抽取方法无法针对金融公告中非结构化人员简历进行人员属性以及事件的抽取,无法发现金融公告中跨文档的人员之间关系.针对以上问题,将非结构化的人员简历抽取成结构化的人员信息模板,提出 ...

  4. 在图数据库Neo4j中创建红楼梦人物关系图谱

    在图数据库Neo4j中创建红楼梦人物关系图谱 1.加载csv数据文件 load csv from 'file:///triples.csv' as linecreate (:role {name:li ...

  5. [转载]红楼梦四大家族人物关系图谱(12系列图表)_RWERWERWE_96921_新浪博客

    原文地址:红楼梦四大家族人物关系图谱(12系列图表) 作者: 静心聆听62 红楼梦主要人物关系 01红楼梦四大家族人物关系图谱 02红楼梦人物关系简图 03红楼梦人物关系详图 04红楼梦四大家族奴隶图 ...

  6. JS组件系列——Bootstrap Table 表格行拖拽(二:多行拖拽)

    原文:JS组件系列--Bootstrap Table 表格行拖拽(二:多行拖拽) 前言:前天刚写了篇JS组件系列--Bootstrap Table 表格行拖拽,今天接到新的需要,需要在之前表格行拖拽的 ...

  7. JS组件系列——Bootstrap Table 表格行拖拽

    JS组件系列--Bootstrap Table 表格行拖拽 原文:JS组件系列--Bootstrap Table 表格行拖拽 前言:之前一直在研究DDD相关知识,好久没更新JS系列文章了.这两天做了一 ...

  8. 解决d3.event在v7版本无效影响zoom拖拽缩放问题

    近期由于代码内关于d3版本的更新,由原来的v3 v4更新值v7.0.0,导致原有关于d3的波点图内zoom方法的拖拽缩放.tooltip提示框问题开始报错,即d3.event数据废弃了 1.设置d3图 ...

  9. 知识图谱初探(二)三体人物关系图谱构建

    <三体>电视剧正在热播,忽然觉得如果用知识图谱来构建下其中的人物关系,想必应该有趣! 那我们就开始吧! 首先列一下其中的相关人物 叶文洁 叶文雪 沙瑞山 杨冬 杨卫宁 白沐霖 邵琳 叶哲泰 ...

最新文章

  1. SQL优化常用方法24
  2. adb安装apk是出现INSTALL_FAILED_INSUFFICIENT_STORAGE
  3. python压缩包怎么安装-python – 如果安装的模块在zip文件的顶层添...
  4. 招聘 Java 和 前端工程师
  5. 爬虫的基本知识第一个请求requests模块的基本使用
  6. Scala中使用两种方式对单词进行次数统计(wordCount)
  7. # hive打不开,提示节点过少,进入安全模式~~
  8. FZU 2108 Mod problem
  9. Jmeter-【JSON Extractor】-响应结果中数组多个相同key取值
  10. python与office结合可以干什么-震惊!当Python遇到Excel后,将开启你的认知虫洞
  11. python3.6安装步骤-python3.6、opencv安装环境搭建过程(图文教程)
  12. 正则正数,负数,整数,浮点数校验大全
  13. Android各版本对应Android API(知识累积)
  14. Linux系统下7个好用的PDF编辑器推荐
  15. PowerDesigner 16逆向工程,MySQL数据库的生成PDM物理数据模型文件
  16. 新氧,今天受了点儿伤
  17. “妈我不想去上课!”“不行你是老师!”
  18. python中用sympy对变量求偏导
  19. 使用该设备需要WIA驱动程序
  20. Pytorch-YOLOV4-火焰目标检测

热门文章

  1. 抖音SEO关键词排名,抖音SEO优化软件
  2. ubuntu16.04 下怎么挂载iso文件
  3. ibm tivoli_在Tivoli Access Manager v6.1 / WebSEAL和Tivoli Integrated Portal v1.1.x之间配置单点登录...
  4. Python3监控IP丢包率
  5. 区块链+房地产带来的交易革命
  6. PyCharm中如何修改文件默认打开方式(以自带数据库db.sqlite3为例)
  7. [附源码]java毕业设计停车场收费管理系统
  8. 电力系统宽频信号处理及实现
  9. 云原生时代,青云QingCloud的“极简”之道
  10. 这十一个副业在家就可以完成,疫情在家也有收入,建议收藏