ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# 力导图 模拟粒子物理运动,甚至可以用来作为一个基本的物理引擎。目前数据展示方面主要用于关系链 如:天眼查 马云公司关系 ![](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)