繁体   English   中英

d3强制布局-如何创建更合理的节点结构

[英]d3 force layout - how to create a more sensible node structure

我有一个d3 force布局,该布局从JSON文件加载节点并从单独的CSV文件加载链接,但是存在几个(可能相关的)问题:

  1. 生成的结构非常均匀地隔开,并且链接中有很多重叠,这使得很难看到连接了哪些节点。 我不确定为什么要得到这样的圆形结构,为什么有些只有1个链接的节点位于其连接节点的相反侧
  2. 当我拖动节点时,所有连接节点都不会随之移动。 除了我要移动的一个节点之外,一切似乎都保持静止,并且该图似乎只是在缓慢放大

您可以在下面的代码段中看到这两个问题:

 svg { display: block; margin: auto; } .links line { stroke: #999; stroke-opacity: 0.6; pointer-events: none; } .nodes circle { stroke: #fff; stroke-width: 1.5px; } text { font-family: sans-serif; font-size: 14px; font-weight: bold; } .primary { color: #af0000; } .secondary { color: #00a700; } div.tooltip { position: absolute; top: 10px; right: 10px; background-color: white; max-width: 200px; height: auto; padding: 10px; border-style: solid; border-radius: 2px; border-width: 1px; box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.5); } div.tooltip .Legendary { color: #ff9b00; } div.tooltip .Epic { color: #ac41c2; } div.tooltip .Elite { color: #058cc3; } div.tooltip .Advanced { color: #2d9830; } div.tooltip table { border: 1px solid black; border-collapse: collapse; } div.tooltip table td { padding: 5px; } 
 <svg width="960" height="700"></svg> <script src="https://d3js.org/d3.v4.min.js"></script> <script> const svg = d3.select('svg'), width = +svg.attr('width'), height = +svg.attr('height'); const color = d3 .scaleOrdinal() .domain(['Legendary', 'Epic', 'Elite', 'Advanced']) .range(['#ff9b00', '#ac41c2', '#058cc3', '#2d9830']); const simulation = d3 .forceSimulation() .force('link', d3.forceLink().id(d => d.id)) .force( 'charge', d3 .forceManyBody() .strength(-20) .distanceMax([500]) ) .force('center', d3.forceCenter(width / 2, height / 2)); d3.queue() .defer(d3.json, 'https://gist.githubusercontent.com/sho-87/bfe4d69be60454dccfd859b004262381/raw/ac259b468ae6f1140d5c10596f1663c743535b5a/commanders.json') .defer(d3.csv, 'https://gist.githubusercontent.com/sho-87/89aa3c48baffc9c388bb98e613b9a5f4/raw/896e24a0187d555d395840d7def3b36b8fb28074/links.csv') .await(function(error, commanders, links) { const nodeByID = d3.map(); commanders.nodes.forEach(commander => { nodeByID.set(commander.id, commander); }); links.forEach(link => { link.source = nodeByID.get(link.primary); link.target = nodeByID.get(link.secondary); }); const link = svg .append('g') .attr('class', 'links') .selectAll('line') .data(links) .enter() .append('line') .attr('stroke-width', d => Math.sqrt(d.value * 1)); const node = svg .selectAll('.node') .data(commanders.nodes) .enter() .append('g') .attr('class', 'nodes') .call( d3 .drag() .on('start', dragstarted) .on('drag', dragged) .on('end', dragended) ) .append('circle') .attr('r', 8) .attr('fill', d => color(d.group)) simulation.nodes(commanders.nodes).on('tick', ticked); simulation.force('link').links(link); function ticked() { link .attr('x1', d => d.source.x) .attr('y1', d => d.source.y) .attr('x2', d => d.target.x) .attr('y2', d => d.target.y); node.attr('transform', d => `translate(${dx},${dy})`); } }); function dragstarted(d) { if (!d3.event.active) simulation.alphaTarget(0.3).restart(); d.fx = dx; d.fy = dy; } function dragged(d) { d.fx = d3.event.x; d.fy = d3.event.y; } function dragended(d) { if (!d3.event.active) simulation.alphaTarget(0); d.fx = null; d.fy = null; } </script> 

我想要的是更不对称的东西,其中相关/连接的节点放置得更近。 理想情况下,我想尽可能减少重叠链接的数量,所以更像这样:

在此处输入图片说明

缩短以下代码

// const nodeByID = d3.map();
// commanders.nodes.forEach(commander => {
//   nodeByID.set(commander.id, commander);
// });
// links.forEach(link => {
//   link.source = nodeByID.get(link.primary);
//   link.target = nodeByID.get(link.secondary);
// });

links = links.map(d => ({source:d.primary, target:d.secondary, value:d.value}));

并将链接对象设置为对象而不是d3选择

simulation.force('link').links(links);

然后,您需要使用链接distancestrength访问器。

 svg { display: block; margin: auto; } .links line { stroke: #999; stroke-opacity: 0.6; pointer-events: none; } .nodes circle { stroke: #fff; stroke-width: 1.5px; } text { font-family: sans-serif; font-size: 14px; font-weight: bold; } .primary { color: #af0000; } .secondary { color: #00a700; } div.tooltip { position: absolute; top: 10px; right: 10px; background-color: white; max-width: 200px; height: auto; padding: 10px; border-style: solid; border-radius: 2px; border-width: 1px; box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.5); } div.tooltip .Legendary { color: #ff9b00; } div.tooltip .Epic { color: #ac41c2; } div.tooltip .Elite { color: #058cc3; } div.tooltip .Advanced { color: #2d9830; } div.tooltip table { border: 1px solid black; border-collapse: collapse; } div.tooltip table td { padding: 5px; } 
 <svg width="960" height="700"></svg> <script src="https://d3js.org/d3.v4.min.js"></script> <script> const svg = d3.select('svg'), width = +svg.attr('width'), height = +svg.attr('height'); const color = d3 .scaleOrdinal() .domain(['Legendary', 'Epic', 'Elite', 'Advanced']) .range(['#ff9b00', '#ac41c2', '#058cc3', '#2d9830']); const simulation = d3 .forceSimulation() .force('link', d3.forceLink().id(d => d.id)) .force( 'charge', d3 .forceManyBody() .strength(-20) .distanceMax([500]) ) .force('center', d3.forceCenter(width / 2, height / 2)); d3.queue() .defer(d3.json, 'https://gist.githubusercontent.com/sho-87/bfe4d69be60454dccfd859b004262381/raw/ac259b468ae6f1140d5c10596f1663c743535b5a/commanders.json') .defer(d3.csv, 'https://gist.githubusercontent.com/sho-87/89aa3c48baffc9c388bb98e613b9a5f4/raw/896e24a0187d555d395840d7def3b36b8fb28074/links.csv') .await(function(error, commanders, links) { // const nodeByID = d3.map(); // commanders.nodes.forEach(commander => { // nodeByID.set(commander.id, commander); // }); // links.forEach(link => { // link.source = nodeByID.get(link.primary); // link.target = nodeByID.get(link.secondary); // }); links = links.map(d => ({source:d.primary, target:d.secondary, value:d.value})); const link = svg .append('g') .attr('class', 'links') .selectAll('line') .data(links) .enter() .append('line') .attr('stroke-width', d => Math.sqrt(d.value * 1)); const node = svg .selectAll('.node') .data(commanders.nodes) .enter() .append('g') .attr('class', 'nodes') .call( d3 .drag() .on('start', dragstarted) .on('drag', dragged) .on('end', dragended) ) .append('circle') .attr('r', 8) .attr('fill', d => color(d.group)) simulation.nodes(commanders.nodes).on('tick', ticked); simulation.force('link').links(links); function ticked() { link .attr('x1', d => d.source.x) .attr('y1', d => d.source.y) .attr('x2', d => d.target.x) .attr('y2', d => d.target.y); node.attr('transform', d => `translate(${dx},${dy})`); } }); function dragstarted(d) { if (!d3.event.active) simulation.alphaTarget(0.3).restart(); d.fx = dx; d.fy = dy; } function dragged(d) { d.fx = d3.event.x; d.fy = d3.event.y; } function dragended(d) { if (!d3.event.active) simulation.alphaTarget(0); d.fx = null; d.fy = null; } </script> 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM