I am trying to change the simulation details dynamically in the D3 example at https://github.com/mbostock/d3/blob/master/examples/force/force-multi-foci.html. I put in a checkbox and then assign the tick handler dynamically as following (full code at http://pastebin.com开发者_如何学Go/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; });
};
But actually the it seems that only the handler given first works. Is there a way to dynamically control the simulation?
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. :)
You can see a great example of this here for d3.v4: https://bl.ocks.org/steveharoz/8c3e2524079a8c440df60c1ab72b5d03
You can see the important functionality here:
// 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();
}
精彩评论