:
赞
踩
本文要实现的功能与这个网页比较类似: KGBuilder知识图谱可视化
使用到的插件为: d3
没有采用echarts等实现的原因是: echarts比较死板, 有些需求不能实现, 而d3可以灵活的制作出想要的图表.
<head></head>
中插入 <script src="https://d3js.org/d3.v5.min.js"></script>
:<head>
<script src="https://d3js.org/d3.v5.min.js"></script>
</head>
npm install d3 --save-dev
或者
cnpm install d3 --save-dev
组件中引入:
import * as d3 from 'd3';
vue中直接使用下面的可能会遇到一些问题, 可以参考: d3 -力引导图(四) vue项目中的使用及可能遇到的问题
在绘制d3图形之前, 有一些基础知识需要掌握, 后续绘制图表的时候才不会太吃力.
主要是 SVG图形 和 d3的一些基础知识.
带 * 号的是推荐必看的, 对d3的基础语法讲解的比较细致清晰, 可以帮助很快上手.
有了一些基础后, 我们来看一个实例, 本实例的来源于: D3.js的v5版本入门教程(第六章)——做一个简单的图表, 如果能理解下面的代码在做什么, 就已经掌握了d3基础的语法.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="https://d3js.org/d3.v5.min.js"></script> </head> <body> <svg></svg> <script> var marge = {top:60,bottom:60,left:0,right:60}//设置边距 var dataset = [ 250 , 210 , 170 , 130 , 90 ]; //数据(表示矩形的宽度) var svg = d3.select("svg");//得到svg画布 var g = svg.append("g")//定义一个用来装整个图表的一个分组,并设置他的位置 .attr("transform","translate("+marge.top+","+marge.left+")"); var rectHeight = 30;//设置每一个矩形的高度 g.selectAll("rect") .data(dataset) .enter() .append("rect") // 添加足够的条形表 .attr("x",20)//设置左上点的x .attr("y",function(d,i){//设置左上点的y, i表示的是索引号 return i*rectHeight; }) .attr("width",function(d){//设置宽 return d; }) .attr("height",rectHeight-5)//设置长 .attr("fill","blue");//颜色填充 </script> </body> </html>
为了绘制力引导图, 我们还需要新学习几个知识点:
如何新建一个力导向图: d3.forceSimulation()
如何添加或者移除一个力: d3.forceSimulation().force()
如何绘制节点
绘制节点采用的是SVG图中的 circle
标签, 阮一峰SVG教程中给出的介绍如下:
如何绘制连线
绘制连线采用的SVG图中的 line
标签, 阮一峰SVG教程中给出的介绍如下:
如何绑定数据: d3绑定数据
如何绘制两个节点之间的连线, 将连线与节点联系起来:
举个例子, 有下面的数据:
当我们绘制节点后, nodes中会新增一些数据:
那么, 我们在绘制连线时, 就可以将连线的起始位置分别设置为两个节点的位置, 来实现在两个节点之间连线的目的.
比如有下面的数据:
我们在绘制连线时, 将 line
标签的 x1,y1,x2,y2
分别设置为source和target节点的x和y, 就可以实现节点间的连线了 (注: 绿色方框这里的n是指的每条连线.)
(其他参考例子: 基础知识二:网页端利用d3.js将json数据进行可视化展示 )
注意: 力引导图要想实现节点之间的连线, 在连线的对象中必须有source和target (分别指向起始节点), 不然会报错; 默认是索引.
下面是两个例子:
(1) 默认是索引
(源代码来自: CSDN)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="https://d3js.org/d3.v5.min.js"></script> </head> <body> <svg width="960" height="600"></svg> <script> var marge = {top:60,bottom:60,left:60,right:60} var svg = d3.select("svg") var width = svg.attr("width") var height = svg.attr("height") var g = svg.append("g") .attr("transform","translate("+marge.top+","+marge.left+")"); //准备数据 var nodes = [ {name:"湖南邵阳",id: 001}, {name:"山东莱州",id: 002}, {name:"广东阳江",id: 003}, {name:"山东枣庄",id: 004}, {name:"泽",id: 005}, {name:"恒",id: 006}, {name:"鑫",id: 007}, {name:"明山",id: 011}, {name:"班长",id: 012} ]; var edges = [ {source:1,target:4,relation:"籍贯",value:1.3}, {source:4,target:5,relation:"舍友",value:1}, {source:4,target:6,relation:"舍友",value:1}, {source:4,target:7,relation:"舍友",value:1}, {source:1,target:6,relation:"籍贯",value:2}, {source:2,target:5,relation:"籍贯",value:0.9}, {source:3,target:7,relation:"籍贯",value:1}, {source:5,target:6,relation:"同学",value:1.6}, {source:6,target:7,relation:"朋友",value:0.7}, {source:6,target:8,relation:"职责",value:2} ]; //设置一个color的颜色比例尺,为了让不同的扇形呈现不同的颜色 var colorScale = d3.scaleOrdinal() .domain(d3.range(nodes.length)) .range(d3.schemeCategory10); //新建一个力导向图 var forceSimulation = d3.forceSimulation() .force("link",d3.forceLink()) .force("charge",d3.forceManyBody()) // .force('collide', d3.forceCollide().strength(-30)) .force("center",d3.forceCenter());; //初始化力导向图,也就是传入数据 //生成节点数据 forceSimulation.nodes(nodes) .on("tick",ticked);//这个函数很重要,后面给出具体实现和说明 //生成边数据 forceSimulation.force("link") .links(edges) .distance(function(d){//每一边的长度 return d.value*100; }) //设置图形的中心位置 forceSimulation.force("center") .x(width/2) .y(height/2); //在浏览器的控制台输出 console.log(nodes); console.log(edges); //有了节点和边的数据后,我们开始绘制 //绘制边 var links = g.append("g") .selectAll("line") .data(edges) .enter() .append("line") .attr("stroke",function(d,i){ return colorScale(i); }) .attr("stroke-width",1); var linksText = g.append("g") .selectAll("text") .data(edges) .enter() .append("text") .text(function(d){ return d.relation; }) //绘制节点 //老规矩,先为节点和节点上的文字分组 var gs = g.selectAll(".circleText") .data(nodes) .enter() .append("g") .attr("transform",function(d,i){ var cirX = d.x; var cirY = d.y; return "translate("+cirX+","+cirY+")"; }) .call(d3.drag() .on("start",started) .on("drag",dragged) .on("end",ended) ); //绘制节点 gs.append("circle") .attr("r",10) .attr("fill",function(d,i){ return colorScale(i); }) //文字 gs.append("text") .attr("x",-10) .attr("y",-20) .attr("dy",10) .text(function(d){ return d.name; }) function ticked(){ links .attr("x1",function(d){return d.source.x;}) .attr("y1",function(d){return d.source.y;}) .attr("x2",function(d){return d.target.x;}) .attr("y2",function(d){return d.target.y;}); linksText .attr("x",function(d){ return (d.source.x+d.target.x)/2; }) .attr("y",function(d){ return (d.source.y+d.target.y)/2; }); gs .attr("transform",function(d) { return "translate(" + d.x + "," + d.y + ")"; }); } function started(d){ if(!d3.event.active){ forceSimulation.alphaTarget(0.8).restart(); } d.fx = d.x; d.fy = d.y; } function dragged(d){ d.fx = d3.event.x; d.fy = d3.event.y; } function ended(d){ if(!d3.event.active){ forceSimulation.alphaTarget(0); } d.fx = null; d.fy = null; } </script> </body> </html>
(2) 设置source和target
(源代码来自: GitHub)
<!DOCTYPE html> <!--draw the graph completely--> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <!--import d3 version 5--> <script type="text/javascript" src="https://d3js.org/d3.v5.min.js"></script> <!--import jquery3.3.1--> <!-- <script type="text/javascript" src="js/jquery-3.3.1.min.js"></script> --> <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script> </head> <body> <svg></svg> <script> //get json file const data = { "nodes": [{ "entity_name": "林黛玉", "entity_id": "entity/2870013", "ontargetlogy_name": "人员", "relation_num": 4 }, { "entity_name": "贾宝玉", "entity_id": "entity/2870127", "ontargetlogy_name": "人员", "relation_num": 2 }], "links": [{ "from": "entity/2870013", "to": "entity/2870127", "relation_id": "relation/815478", "relation_name": "表哥" }] } //GroupExplorer constructing function //this is one way to create a javascript object function GroupExplorer(data) { console.log(data) //create an object-include some data //this is an another way to create a javascript object var defaultConfig = { windowWidth: window.innerWidth, windowHeight: window.innerHeight, defaultLinkDistance: 150, data: data } console.log(defaultConfig) var svg = d3.select("svg"); svg.attr("width", defaultConfig.windowWidth); svg.attr("height", defaultConfig.windowHeight); defaultConfig.data.links.forEach(function (e) { var sourceNode = defaultConfig.data.nodes.filter(function (n) { return n.entity_id === e.from; })[0]; var targetNode = defaultConfig.data.nodes.filter(function (n) { return n.entity_id === e.to; })[0]; e.source = sourceNode; e.target = targetNode; console.log(e) }); //create a force graph var forceSimulation = d3.forceSimulation() .force("link", d3.forceLink()) .force("charge", d3.forceManyBody()) .force("center", d3.forceCenter(defaultConfig.windowWidth / 2, defaultConfig.windowHeight / 2)); //transform nodes data forceSimulation.nodes(defaultConfig.data.nodes) .on("tick", ticked); //tranform links data forceSimulation.force("link") .links(defaultConfig.data.links) .distance(defaultConfig.defaultLinkDistance); console.log(defaultConfig.data.nodes); console.log(defaultConfig.data.links); //define arrow svg.append("svg:defs") .append("svg:marker") .attr("id", "marker") .attr('viewBox', '0 -5 10 10') .attr("refX", 20) .attr("refY", 0) .attr('markerWidth', 10) .attr('markerHeight', 10) .attr('orient', 'auto') .append('svg:path') .attr('d', 'M0,-5L10,0L0,5') .attr("fill", "grey"); //draw links var links = svg.append("g") .selectAll("line") .data(defaultConfig.data.links) .enter() .append("line") .attr("x1", function (n) { return n.source.x }) .attr("y1", function (n) { return n.source.y }) .attr("x2", function (n) { return n.target.x }) .attr("y2", function (n) { return n.target.y }) .attr("stroke", "grey") .attr("stroke-width", 1) .attr("marker-end", "url(#marker)"); //draw links-text var links_text = svg.append("g") .selectAll("text") .data(defaultConfig.data.links) .enter() .append("text") .attr("x", function (e) { return (e.source.x + e.target.x) / 2; }) .attr("y", function (e) { console.log(e.source.y + "+" + e.target.y) return (e.source.y + e.target.y) / 2; }) .attr("font-size", 10) .text(function (e) { return e.relation_name }); //draw nodes group = node+node-text var nodes_g = svg.append("g") .selectAll("g") .data(defaultConfig.data.nodes) .enter() .append("g") .attr("transform", function (e) { return "translate(" + e.x + "," + e.y + ")"; }) .call(d3.drag() .on("start", started) .on("drag", dragged) .on("end", ended)); //draw nodes nodes_g.append("circle") .attr("r", function (e) { return e.relation_num * 5 }) .attr("fill", "yellow"); //draw node-text nodes_g.append("text") .attr("x", -15) .attr("y", 20) .attr("font-size", 10) .text(function (e) { return e.entity_name }); function started(d) { if (!d3.event.active) { forceSimulation.alphaTarget(0.8).restart(); } d.fx = d.x; d.fy = d.y; } function dragged(d) { d.fx = d3.event.x; d.fy = d3.event.y; } function ended(d) { if (!d3.event.active) { forceSimulation.alphaTarget(0); } d.fx = null; d.fy = null; } function ticked() { links .attr("x1", function (n) { return n.source.x }) .attr("y1", function (n) { return n.source.y }) .attr("x2", function (n) { return n.target.x }) .attr("y2", function (n) { return n.target.y }) links_text .attr("x", function (e) { return (e.source.x + e.target.x) / 2; }) .attr("y", function (e) { return (e.source.y + e.target.y) / 2; }) nodes_g .attr("transform", function (e) { return "translate(" + e.x + "," + e.y + ")"; }) } } //because in the way of creating a javascript object, //you need to use "new" to use it new GroupExplorer(data); </script> </body> </html>
通过上面两段代码 (特别是林黛玉和贾宝玉的例子), 引用接口数据已经可以简单的绘制一个力引导图了.
更多内容可以查看我的d3专栏: d3绘制力引导图
下一篇: d3 - 力引导图(二) 节点多种颜色方案
参考源码:
KGBuilder GitHub源码
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。