# 力导图
模拟粒子物理运动,甚至可以用来作为一个基本的物理引擎。目前数据展示方面主要用于关系链
如:天眼查 马云公司关系
![](https://box.kancloud.cn/706f69497fb11ddc11ccac6e1f5ccce5_878x521.png)
## 需求
已知有节点['A','B','C','D','AB','AC','BD']
其中含相同字母的连接:
```text
A - AB
B - AB
A - AC
C - AC
B - BD
D - BD
```
## 分析
首先建议阅读[力导图-中文](https://github.com/xswei/d3-force#simulation_nodes)
先说说d3-force。
力模拟器d3.forceSimulation,通过指定的node和各种力来模拟出node的位置。
力的模型有:
forceCenter:中心力
forceLink:弹簧模型
forceCollide:用于碰撞检测式力
forceManyBody:电荷力
forceX和forceY:定位力模型可以将节点沿着指定的维度进行排列
forceRadial:环形力的参考位置是一个闭合的环
接下来需要构建数据:
```javascript
var nodes_data = [{name:'A'},
{name:'B'},{name:'C'},{name:'D'},
{name:'AB'},{name:'AC'},{name:'BD'}];
var edges_data = [
{source:0,target:4 },
{source:1,target:4 },
{source:0,target:5 },
{source:2,target:5 },
{source:1,target:6 },
{source:3,target:6 }
];
```
分析:
1. 为了使图居中所以使用forceCenter力设置中心位置
2. 为了使用节点之间有拖拽弹性所以使用forceLink
3. 为了使节点之间不重叠使用电荷力forceManyBody
### 解析
1. 力导图和其他布局有所区别:其他布局是一次计算好的,而力导图是一个模拟力作用的动态变化。
2. 重点是我们需要配置好force后,设置回调tick,当某点的位置发生变化,会使力场发生变化,此时的force会不断的回调tick,而我们需要在tick中去更新如圆,连线,文字位置等。
## 绘制
``` javascript
//创建svg
var svg = d3.select('#root')
.append('svg')
.attr('width', 600)
.attr('height', 600)
.style("background-color","rgb(142, 137, 137)");
var margin=[100,100,100,100]
//构建颜色比例尺
var color = d3.scaleOrdinal(d3.schemeCategory20)
//模拟数据:
var nodes_data = [{name:'A'},
{name:'B'},{name:'C'},{name:'D'},
{name:'AB'},{name:'AC'},{name:'BD'}];
var edges_data = [
{source:0,target:4 },
{source:1,target:4 },
{source:0,target:5 },
{source:2,target:5 },
{source:1,target:6 },
{source:3,target:6 }
];
var simulation = d3.forceSimulation(nodes_data)
.force("charge", d3.forceManyBody().strength(-500))
.force("link", d3.forceLink(edges_data).distance(20).strength(1))
.force("center", d3.forceCenter(300, 300))
.on('tick', tick)
//绘制links
var link = svg.selectAll("g.link")
.data(edges_data)
.enter()
.append('g')
.attr("class", 'link')
.append('line')
.attr('stroke', '#000')
.attr('stroke-width', 1)
.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 })
var dragged = d3.drag()
.container(document.getElementsByTagName("svg")[0])
.on('start', function (d) {
if (!d3.event.active) simulation.alphaTarget(0.6).restart();
d.fx = d.x;
d.fy = d.y;
}) //mousedown
.on('drag', function (d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}) //mousemove
.on('end', function (d) {
console.log(d3.event.subject)
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}) //mousedown
//绘制节点
var nodes = svg.selectAll("g.nodes")
.data(nodes_data)
.enter()
.append("g")
.attr("class", "nodes")
.call(dragged)
var node = nodes.append("circle")
.attr("cx", function (d) { return d.x })
.attr("cy", function (d) { return d.y })
.attr("r", 20)
.attr('fill', function (d, i) { return color(i) })
var nodeT = nodes.append('text')
.attr('x',function (d) { return d.x })
.attr("y", function (d) { return d.y })
.text(function(d){console.log(d); return d.name})
//当力模拟完成后修改显示状态
function tick() {
link.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 })
node.attr("cx", function (d) { return d.x })
.attr("cy", function (d) { return d.y })
nodeT.attr('x',function (d) {
var textL = d.name.length;
return d.x-((textL/2)*10)
})
.attr("y", function (d) { return d.y+5 })
}
```
## 实例
[force0](https://doter1995.github.io/d3-start-course/force/force-0.html)
关于文字居中在圆心,x通过拿到文本长度计算偏移。缺点是文字出现长度为0,是比较尴尬。
[force1](https://doter1995.github.io/d3-start-course/force/force-1.html)
关于文字居中在圆心,这个使用text-anchor的middle即可自动处理偏移。所以只需要为y添加偏移即可。
![](https://box.kancloud.cn/5555864a5b29c66ba3449cec829ad56d_436x508.png)