手柄与d3.js多焦点力布局碰撞

时间:2016-08-18 07:42:19

标签: d3.js

我是d3.js的新人,我试图做的就是接近这个:http://codepen.io/fabiobiondi/pen/nFxyD

// GROUPS:  0 Web | 1: Adobe | 2: hybrid
var data = [
  {"id": 0, "name": "AngularJS", "r": 50 },
  {"id": 0, "name": "HTML5", "r": 40 },
  {"id": 0, "name": "Javascript", "r": 30 },
  {"id": 0, "name": "NodeJs", "r": 30 },
  {"id": 0, "name": "D3.js", "r": 40 },
  {"id": 0, "name": "CreateJS", "r": 45 },
  {"id": 0, "name": "Cordova", "r": 40 },
  {"id": 0, "name": "CSS", "r": 40 },
  {"id": 0, "name": "SVG", "r": 20 },
  {"id": 0, "name": "PHP", "r": 20 },
  {"id": 0, "name": "jQuery", "r": 30 },

  {"id": 1, "name": "Actionscript", "r": 50 },
  {"id": 1, "name": "Flash", "r": 32 },
  {"id": 1, "name": "Flex", "r": 50 },
  {"id": 1, "name": "AIR", "r": 40 },
  {"id": 1, "name": "Photoshop", "r": 30 },
  {"id": 1, "name": "Illustrator", "r": 30 },

  {"id": 2, "name": "Node Webkit", "r": 40 },
  {"id": 2, "name": "Chrome App", "r": 30 },
  {"id": 2, "name": "Cordova", "r": 45 },
];

var width = window.innerWidth,
    height = 450;

var fill = d3.scale.category10();

var nodes = [], labels = [],
    foci = [{x: 0, y: 150}, {x: 350, y: 150}, {x: 200, y: 150}];

var svg = d3.select("body").append("svg")
    .attr("width", "100%")
    .attr("height", height)
    //.attr("domflag", '');

var force = d3.layout.force()
    .nodes(nodes)
    .links([])
    .charge(-400)
    //.chargeDistance(200)
    .gravity(0.1)
    .friction(0.8)
    .size([width, height])
    .on("tick", tick);

//var node = svg.selectAll("circle");
var node = svg.selectAll("g");

var counter = 0;

function tick(e) {
  var k = .1 * e.alpha;

  // Push nodes toward their designated focus.
  nodes.forEach(function(o, i) {
    o.y += (foci[o.id].y - o.y) * k;
    o.x += (foci[o.id].x - o.x) * k;
  });

  node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });

}


var timer = setInterval(function(){

  if (nodes.length > data.length-1) { clearInterval(timer); return;}

  var item = data[counter];
  nodes.push({id: item.id, r: item.r, name: item.name});
  force.start();

  node = node.data(nodes);

  var n = node.enter().append("g")
      .attr("class", "node")
      .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
      .style('cursor', 'pointer')
      .on('mousedown', function() {
         var sel = d3.select(this);
         sel.moveToFront();
      })
      .call(force.drag);

  n.append("circle")
      .attr("r",  function(d) { return d.r; })
      .style("fill", function(d) { return fill(d.id); })

  n.append("text")
      .text(function(d){
          return d.name;
      })
      .style("font-size", function(d) {
          return Math.min(2 * d.r, (2 * d.r - 8) / this.getComputedTextLength() * 16) + "px"; 
       })
      .attr("dy", ".35em")

  counter++;
}, 100);


d3.selection.prototype.moveToFront = function() {
  return this.each(function(){
    this.parentNode.appendChild(this);
  });
};

function resize() {
  width = window.innerWidth;
  force.size([width, height]);
  force.start();
}

d3.select(window).on('resize', resize);

我不明白的是我如何控制圈子能够或不能碰撞的方式。

我尝试了碰撞功能https://bl.ocks.org/mbostock/1804919,但我不知道它是如何工作的。当我更改填充值时,圆圈会自行避开。我想要的是允许碰撞而不是太多(让我们说10px),以确保内部文本永远不会被隐藏。

1 个答案:

答案 0 :(得分:1)

我已经实现了这个示例:https://bl.ocks.org/mbostock/7881887

我添加了碰撞功能:

// Resolves collisions between d and all other circles.
function collide(alpha) {
  var quadtree = d3.geom.quadtree(nodes);
  return function(d) {
    //console.log(d)
    var r = d.r + maxRadius + Math.max(padding, clusterPadding),
        nx1 = d.x - r,
        nx2 = d.x + r,
        ny1 = d.y - r,
        ny2 = d.y + r;
    quadtree.visit(function(quad, x1, y1, x2, y2) {
      if (quad.point && (quad.point !== d)) {
        var x = d.x - quad.point.x,
            y = d.y - quad.point.y,
            l = Math.sqrt(x * x + y * y),
            r = d.r + quad.point.r + (d.cluster === quad.point.cluster ? padding : clusterPadding);
        if (l < r) {
          l = (l - r) / l * alpha;
          d.x -= x *= l;
          d.y -= y *= l;
          quad.point.x += x;
          quad.point.y += y;
        }
      }
      return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
    });
  };
}

这一点在上面的链接中都有解释,所以我不需要在这里解释。

在示例中它的d.radius,但你有d.r.Also maxRadius,我这样做:

var maxRadius = data.reduce(function(sum, d){
  return Math.max(sum,d.r)
}, 0); //get maximum radius

以上减少了数据数组并获得了r(半径)的最大值。它们是我所做的唯一改变。

我已经更新了你的滴答功能:

function tick(e) {
  var k = .1 * e.alpha;

  // Push nodes toward their designated focus.
  nodes.forEach(function(o, i) {
   // console.log(o)
    o.y += (foci[o.id].y - o.y) * k;
    o.x += (foci[o.id].x - o.x) * k;
  });

  node
    .each(collide(.5)) //call collide function here
    .attr("cx", function(d) { return d.x; })
      .attr("cy", function(d) { return d.y; })
    .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });

}

这会照顾你所拥有的多个焦点和碰撞检测。

更新了小提琴:https://jsfiddle.net/thatOneGuy/392kwuru/