1

我正在尝试在https://github.com/mbostock/d3/blob/master/examples/force/force-multi-foci.html的D3示例中动态更改模拟细节。我输入了一个复选框,然后按如下方式动态分配刻度处理程序(完整代码位于http://pastebin.com/k4P0uzHK):

$("#chkBox").change(function(){
  if ($(this).is(':checked')) {
    force.on("tick", forceTick);
  } else {
    force.on("tick", forceTick2);
  }
});

forceTick = function(e) {
  // Push different nodes in different directions for clustering.
  var ky = 400 * e.alpha;
  var kx = 20 * e.alpha;
  hLinks.forEach(function(hlink) {
    var yB = hlink.source.y, yT = hlink.target.y;
    if (yB<(yT+20)) { hlink.source.y += Math.min(ky,yT+20-yB); hlink.target.y -= Math.min(ky,yT+20-yB);}
    var xB = hlink.source.x, xT = hlink.target.x;
    if (xB<(xT-20)) { hlink.source.x += Math.min(kx,xT-20-xB); hlink.target.x -= Math.min(kx,xT-20-xB);}
    if (xB>(xT+20)) { hlink.source.x -= Math.min(kx,xB-xT-20); hlink.target.x += Math.min(kx,xB-xT-20);}

  });

  node.attr("cx", function(d) { return d.x; })
      .attr("cy", function(d) { return d.y; });

  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; });
};


forceTick2 = function(e) {

  node.attr("cx", function(d) { return d.x; })
      .attr("cy", function(d) { return d.y; });

  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; });
};

但实际上,似乎只有首先给出的处理程序才有效。有没有办法动态控制模拟?

4

2 回答 2

4

The on operator for force layouts (and anything else using d3.dispatch) adds an event listener. It doesn’t replace an existing event listener. The force layout doesn’t currently expose a mechanism to remove or replace an existing event listener.

That’s a bug. I intend to make the layout’s on operator consistent with selections, which allow you to add, replace and remove event listeners by calling on multiple times. You’ll still be able to register multiple listeners if you use namespaces (such as "tick.foo" and "tick.bar").

In the meantime, the simple fix is to use a single method as your tick listener, but then use some global boolean to determine which of the two behaviors you want to take for each tick. In your case, something like:

if (checked) {
  … clustering …
}
… update link positions …
… update node positions …

And plus, that eliminates the code duplication. :)

于 2011-09-15T03:40:06.833 回答
1

您可以在此处查看 d3.v4 的一个很好的示例: https ://bl.ocks.org/steveharoz/8c3e2524079a8c440df60c1ab72b5d03

您可以在此处查看重要功能:

// add forces to the simulation
function initializeForces() {
  // add forces and associate each with a name
  simulation
    .force("link", d3.forceLink())
    .force("charge", d3.forceManyBody())
    .force("collide", d3.forceCollide())
    .force("center", d3.forceCenter())
    .force("forceX", d3.forceX())
    .force("forceY", d3.forceY());
  // apply properties to each of the forces
  updateForces();
}

// apply new force properties
function updateForces() {
  // get each force by name and update the properties
  simulation.force("center")
    .x(width * forceProperties.center.x)
    .y(height * forceProperties.center.y);
  simulation.force("charge")
    .strength(forceProperties.charge.strength * forceProperties.charge.enabled)
    .distanceMin(forceProperties.charge.distanceMin)
    .distanceMax(forceProperties.charge.distanceMax);
  simulation.force("collide")
    .strength(forceProperties.collide.strength * forceProperties.collide.enabled)
    .radius(forceProperties.collide.radius)
    .iterations(forceProperties.collide.iterations);
  simulation.force("forceX")
    .strength(forceProperties.forceX.strength * forceProperties.forceX.enabled)
    .x(width * forceProperties.forceX.x);
  simulation.force("forceY")
    .strength(forceProperties.forceY.strength * forceProperties.forceY.enabled)
    .y(height * forceProperties.forceY.y);
  simulation.force("link")
    .id(function(d) {return d.id;})
    .distance(forceProperties.link.distance)
    .iterations(forceProperties.link.iterations)
    .links(forceProperties.link.enabled ? graph.links : []);

  // updates ignored until this is run
  // restarts the simulation (important if simulation has already slowed down)
  simulation.alpha(1).restart();
}
于 2017-08-24T15:06:16.743 回答