d3.js将可拖动网络与力导向图组合在一起

时间:2015-01-23 15:59:07

标签: javascript d3.js

我尝试允许用户使用d3的强制导向图选择和拖动多个节点,但似乎无法让它工作。

有一个很好的例子,说明如何选择多个节点并将它们拖动here和标准的力导向图here

当我尝试将两者结合起来时,拖动多个节点似乎有点偏离,因为节点不会被拖到一起,而是一个被拖动而另一个被选中的节点在它周围摆动:

http://jsfiddle.net/pkerpedjiev/w4a5makd/2/

有人能让我知道我做错了什么吗?

以下是代码:

var width = 960,
    height = 500,
    shiftKey;

var svg = d3.select("body")
    .attr("tabindex", 1)
    .on("keydown.brush", keydown)
    .on("keyup.brush", keyup)
    .each(function() { this.focus(); })
  .append("svg")
    .attr("width", width)
    .attr("height", height);

var link = svg.append("g")
    .attr("class", "link")
  .selectAll("line");

var brush = svg.append("g")
    .datum(function() { return {selected: false, previouslySelected: false}; })
    .attr("class", "brush");

var node = svg.append("g")
    .attr("class", "node")
  .selectAll("circle");

  graph.links.forEach(function(d) {
    d.source = graph.nodes[d.source];
    d.target = graph.nodes[d.target];
  });

  link = link.data(graph.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; });

  brush.call(d3.svg.brush()
        .x(d3.scale.identity().domain([0, width]))
        .y(d3.scale.identity().domain([0, height]))
        .on("brushstart", function(d) {
          node.each(function(d) { d.previouslySelected = shiftKey && d.selected; });
        })
        .on("brush", function() {
          var extent = d3.event.target.extent();
          node.classed("selected", function(d) {
            return d.selected = d.previouslySelected ^
                (extent[0][0] <= d.x && d.x < extent[1][0]
                && extent[0][1] <= d.y && d.y < extent[1][1]);
          });
        })
        .on("brushend", function() {
          d3.event.target.clear();
          d3.select(this).call(d3.event.target);
        }));

var force = d3.layout.force()
        .charge(-120)
        .linkDistance(30)
        .nodes(graph.nodes)
        .links(graph.links)
        .size([width, height])
        .start();

  node = node.data(graph.nodes).enter().append("circle")
      .attr("r", 4)
      .attr("cx", function(d) { return d.x; })
      .attr("cy", function(d) { return d.y; })
      .on("mousedown", function(d) {
        if (!d.selected) { // Don't deselect on shift-drag.
          if (!shiftKey) node.classed("selected", function(p) { return p.selected = d === p; });
          else d3.select(this).classed("selected", d.selected = true);
        }
      })
      .on("mouseup", function(d) {
        if (d.selected && shiftKey) d3.select(this).classed("selected", d.selected = false);
      })
      .call(force.drag()
        .on("drag", function(d) { nudge(d3.event.dx, d3.event.dy); }));

      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', d.x).attr('cy', d.y);

      };

      force.on("tick", tick);

function nudge(dx, dy) {
  node.filter(function(d) { return d.selected; })
      .attr("cx", function(d) { return d.x += dx; })
      .attr("cy", function(d) { return d.y += dy; })

  link.filter(function(d) { return d.source.selected; })
      .attr("x1", function(d) { return d.source.x; })
      .attr("y1", function(d) { return d.source.y; });

  link.filter(function(d) { return d.target.selected; })
      .attr("x2", function(d) { return d.target.x; })
      .attr("y2", function(d) { return d.target.y; });

  d3.event.sourceEvent.preventDefault();
}

function keydown() {
  if (!d3.event.metaKey) switch (d3.event.keyCode) {
    case 38: nudge( 0, -1); break; // UP
    case 40: nudge( 0, +1); break; // DOWN
    case 37: nudge(-1,  0); break; // LEFT
    case 39: nudge(+1,  0); break; // RIGHT
  }
  shiftKey = d3.event.shiftKey || d3.event.metaKey;
}

function keyup() {
  shiftKey = d3.event.shiftKey || d3.event.metaKey;
}

1 个答案:

答案 0 :(得分:1)

想出来。诀窍是覆盖默认的force.drag行为。默认情况下,拖动行为将被拖动的节点标记为固定并直接更改其位置。这是为了防止它被拖动时被其他节点拉动。

当选择多个节点时,所有节点都必须修复,我们必须直接更改其位置(所有pxpyx和{{1} })。这允许我们一次拖动多个节点。请看这里的小提琴:

http://jsfiddle.net/pkerpedjiev/29majy5c/2/

连同相应的代码:

y