问题
I'm making an interactive bubble chart and I'm working on functionality to split the data into two groups which move to opposite sides of the screen. I'm using a centering force for my simulation because I think it gives a much nicer and more consistent display of the data than using forceX and forceY does. However, I'm having trouble with the splitting of my data.
I had the idea that, since you can pass an anonymous function as a parameter to forceX to determine whether a node moves left or right, you could theoretically do the same thing for the x value in a centering force. My center force code looks like this:
var forceCenterSplit = d3.forceCenter(function(d) {
if (d[splitParameter] >= splitVal)
return 3*width/4;
else
return width/4;
}, height/2)
For comparison, here is the code for the forceX that accomplishes the same thing:
var forceXsplit = d3.forceX(function(d) {
if (d[splitParameter] >= splitVal)
return 3*width/4;
else
return width/4;
}).strength(.05);
Unfortunately, the console is saying "Unexpected value NaN parsing cx attribute." when I run the centering force and is pushing all of the data to cx = 0 (the default value).
Am I missing something basic here? Can you not pass an anonymous function as a parameter to the centering force? If not, is there a better way to do this?
Thanks!
// nicer looking splitting forces that use forceCenter
var forceCenterCombine = d3.forceCenter(width/2, height/2);
var forceCenterSplit = d3.forceCenter(function(d) {
if (d[splitParameter] >= splitVal)
return 3*width/4;
else
return width/4;
}, height/2);
// simple splitting forces that only use forceX
var forceXSplit = d3.forceX(function(d) {
if (d[splitParameter] >= splitVal)
return 3*width/4;
else
return width/4;
}).strength(.05);
var forceXCombine = d3.forceX(width/2).strength(.05);
// collision force to stop the bubbles from hitting each other
var forceCollide = d3.forceCollide(function(d){
console.log("forceCollide");
return radiusScale(d[radiusParam]) + 1;
}).strength(.75)
// This code is for the simulation that combines all the forces
var simulation = d3.forceSimulation()
.force("center", forceCenterCombine)
.force("collide", forceCollide)
.on('end', function(){console.log("Simulation ended!");});
function ticked() {
circles
.attr("cx", function(d){
return d.x;
})
.attr("cy", function(d){
return d.y;
})
}
var splitFlag = false;
// dynamically divide the bubbles into two (or probably more later on) groups
$scope.split = function() {
// split them apart
if (!splitFlag){
console.log("splitForce");
simulation.force("center", forceXSplit)
.force("y", d3.forceY(height/2).strength(.05))
.alphaTarget(.25)
.restart();
splitFlag = true;
}
// bring them back together
else {
console.log("combineForce");
simulation.force("center", forceCenterCombine)
.alphaTarget(.25)
.restart();
splitFlag = false;
}
};
回答1:
Unfortunately, the answer seems to be no.
Due to the very nature of d3.forceCenter
, that ("pass an anonymous function as a parameter") is not possible. The API says:
The centering force translates nodes uniformly so that the mean position of all nodes (the center of mass if all nodes have equal weight) is at the given position ⟨x,y⟩. (emphasis mine)
Thus, there is no space for an accessor function here. forceX
and forceY
, on the other hand...
set the coordinate accessor to the specified number or function. (emphasis mine again)
... and may suit you best.
来源:https://stackoverflow.com/questions/41843166/d3-js-pass-anonymous-function-as-parameter-to-centering-force