ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

javascript – D3.js如何将力布局的节点排列在圆上

2019-07-03 09:19:54  阅读:380  来源: 互联网

标签:javascript d3-js force-layout


我已经开发了一种力量布局来表示社会群体之间的关系.现在我想让节点分布在一个带有连接它们的链接的圆圈中.做这个的最好方式是什么?

代码的完整版本(没有数据)在这里http://jsfiddle.net/PatriciaW/zZSJT/
(为什么我也必须在这里包含代码?这是主要部分)

d3.json("/relationships?nocache=" + (new Date()).getTime(),function(error,members){
   var links=members.organizations.map(function(members) {
      return members.member;
      });

var nodes = {};

links.forEach(function(link) {
  link.source = nodes[link.xsource] || (nodes[link.xsource] = {source: link.xsource, name: link.xsource, category: link.categorysource, path: link.pathsource, desc: link.descsource, title: link.titlesource});
  link.target = nodes[link.xtarget] || (nodes[link.xtarget] = {target: link.xtarget, name: link.xtarget, category: link.categorytarget, path: link.pathtarget, desc: link.desctarget, title: link.titletarget});
});

force = d3.layout.force()
.nodes(d3.values(nodes))
.links(links)
.size([width, height])
.charge(-120)
.linkDistance(function() {return (Math.random() * 200) + 100;})
.linkStrength(0.5)
.on("tick", tick)
.start();

var link = svg.selectAll(".link")
.data(force.links())
.enter().append("line")
.attr("class", "link");

var node_drag = d3.behavior.drag()
    .on("dragstart", dragstart)
    .on("drag", dragmove)
    .on("dragend", dragend);

var loading = svg.append("text")
.attr("x", width / 2)
.attr("y", height / 2)
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text("Simulating. One moment please…");

function dragstart(d, i) {
    force.stop() // stops the force auto positioning before you start dragging
}

function dragmove(d, i) {
    d.px += d3.event.dx;
    d.py += d3.event.dy;
    d.x += d3.event.dx;
    d.y += d3.event.dy; 
    tick(); // this is the key to make it work together with updating both px,py,x,y on d !
}

function dragend(d, i) {
    d.fixed = true; // of course set the node to fixed so the force doesn't include the node in its auto positioning stuff
    tick();
    force.resume();
};

var node = svg.selectAll(".node")
.data(force.nodes())
.enter().append("g")
.attr("class", "node")
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.on("click", clickAlert)
.call(node_drag);

node.append("circle")
.attr("r", 8)
.style("fill", function(d) { 
   return categoryColour [d.category];
   })  

// add an image marker
node.append("image")
  .attr("x",-8)
  .attr("y",-8)
  .attr("width", 16)
  .attr("height", 16)
  .attr("xlink:href", function(d) {
      return categoryImage [d.category]
   })
  .on("click", clickAlert)
  .style("cursor", "pointer")

node.append("text")
  .attr("x", 12)
  .attr("dy", ".35em")
  .text(function(d) { 
      return d.name; 
      });
// Use a timeout to allow the rest of the page to load first.
setTimeout(function() {

// Run the layout a fixed number of times.
// The ideal number of times scales with graph complexity.
force.start();
for (var i = n * n; i > 0; --i) force.tick();
force.stop();

svg.selectAll("line")
.data(links)
.enter().append("line")
.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; });

svg.selectAll("circle")
.data(nodes)
.enter().append("circle")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", 4.5);

loading.remove();
}, 10);

function tick() {
link
  .attr("x1", function(d) { 
       return d.source.x + xadj; })
  .attr("y1", function(d) { 
       return d.source.y + yadj; })
  .attr("x2", function(d) { 
       return d.target.x +xadj; })
  .attr("y2", function(d) { 
       return d.target.y +yadj; });
node
  .attr("transform", function(d) { 
       return "translate(" + (d.x + xadj) + "," + (d.y + yadj) + ")"; 
  });
};

function mouseover() {
 d3.select(this).select("circle").transition()
  .duration(750)
  .attr("r", 16);
d3.select(this).select("text")
   .attr("font-size","34px")
   .style("font-weight", "bold");
};

function mouseout() {
d3.select(this).select("circle").transition()
 .duration(750)
 .attr("r", 8);
d3.select(this).select("text")
  .attr("font-size","12px")
  .style("font-weight", "normal");
};
}) // end json

解决方法:

这是someone else’s solution

This network graph uses the D3 force layout to draw nodes and links, but instead of using d3.force() to find the best node positions, we draw an invisible arc and evenly places nodes along the circumference.

<!DOCTYPE html>
<html>
<head>
<script src="http://d3js.org/d3.v3.min.js"></script>
  <meta charset="utf-8">
  <title>JS Bin</title>

  <style>
  line.node-link, path.node-link {
    fill: none;
    stroke: black
  }
  circle.node {
    fill: white;
    stroke: black
  }
  circle.node+text {
    text-anchor: middle;
  }
  text {
    font-family: sans-serif;
    pointer-events: none;
  }

  </style>
</head>
<body>
<script type="text/javascript">
// number of random nodes (gets crowded at >25 unless you change node diameter)
var num = 20;

// returns random int between 0 and num
function getRandomInt() {return Math.floor(Math.random() * (num));}

// nodes returns a [list] of {id: 1, fixed:true}
var nodes = d3.range(num).map(function(d) { return {id: d}; });

// links returns a [list] of {source: 0, target: 1} (values refer to indicies of nodes)
var links = d3.range(num).map(function(d) { return {source: getRandomInt(), target: getRandomInt()}; });

var width = 500,
    height = 500;

var force = d3.layout.force()
    .nodes(nodes)
    .links(links)
    .size([width, height]);

// evenly spaces nodes along arc
var circleCoord = function(node, index, num_nodes){
    var circumference = circle.node().getTotalLength();
    var pointAtLength = function(l){return circle.node().getPointAtLength(l)};
    var sectionLength = (circumference)/num_nodes;
    var position = sectionLength*index+sectionLength/2;
    return pointAtLength(circumference-position)
}

// fades out lines that aren't connected to node d
var is_connected = function(d, opacity) {
    lines.transition().style("stroke-opacity", function(o) {
        return o.source === d || o.target === d ? 1 : opacity;
    });
}

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);


// invisible circle for placing nodes
// it's actually two arcs so we can use the getPointAtLength() and getTotalLength() methods
var dim = width-80
var circle = svg.append("path")
    .attr("d", "M 40, "+(dim/2+40)+" a "+dim/2+","+dim/2+" 0 1,0 "+dim+",0 a "+dim/2+","+dim/2+" 0 1,0 "+dim*-1+",0")
    .style("fill", "#f5f5f5");

force.start();

// set coordinates for container nodes
nodes.forEach(function(n, i) {
    var coord = circleCoord(n, i, nodes.length)
    n.x = coord.x
    n.y = coord.y
});

// use this one for straight line links...
// var lines = svg.selectAll("line.node-link")
//   .data(links).enter().append("line")
//     .attr("class", "node-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; });

// ...or use this one for curved line links
var lines = svg.selectAll("path.node-link")
    .data(links).enter().append("path")
    .attr("class", "node-link")
    .attr("d", function(d) {
        var dx = d.target.x - d.source.x,
            dy = d.target.y - d.source.y,
            dr = Math.sqrt(dx * dx + dy * dy);
        return "M" + 
            d.source.x + "," + 
            d.source.y + "A" + 
            dr + "," + dr + " 0 0,1 " + 
            d.target.x + "," + 
            d.target.y;
    });

var gnodes = svg.selectAll('g.gnode')
    .data(nodes).enter().append('g')
    .attr("transform", function(d) {
        return "translate("+d.x+","+d.y+")"
    })
    .classed('gnode', true);

var node = gnodes.append("circle")
    .attr("r", 25)
    .attr("class", "node")
    .on("mouseenter", function(d) {
        is_connected(d, 0.1)
        node.transition().duration(100).attr("r", 25)
        d3.select(this).transition().duration(100).attr("r", 30)
    })
    .on("mouseleave", function(d) {
        node.transition().duration(100).attr("r", 25);
        is_connected(d, 1);
    });  

var labels = gnodes.append("text")
    .attr("dy", 4)
    .text(function(d){return d.id})
</script>

</body>
</html>

标签:javascript,d3-js,force-layout
来源: https://codeday.me/bug/20190703/1365428.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有