d3.js: Pass anonymous function as parameter to cen

2019-06-26 18:37发布

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!

Start state

After split

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

enter image description here

1条回答
Bombasti
2楼-- · 2019-06-26 18:56

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.

查看更多
登录 后发表回答