简体   繁体   中英

Updating D3 bubble radius with collide simulation

I've been wrestling very hard with D3 to try to make a simple bubble-chart using force collide that live-updates the bubble size.

I can get the chart to show on the first data update with force collide. However subsequent data calls freeze the chart and the sizes are never updated:

https://jsfiddle.net/d2zcfjfa/1/

node = svg.selectAll('g.node')
    .data(root.children)
    .enter()
    .append('g')
    .attr('class', 'node')
    .append('circle')
    .attr('r', function(d) { return d.r * 1.4; })
    .attr('fill', function(d) { return color(d.data.name); })
    .call(d3.drag()
        .on("start", dragStart)
        .on("drag", dragged)
        .on("end", dragEnd));

var circleUpdate = node.select('circle')
    .attr('r', function(d)
    {
        return d.r;
    });

simulation.nodes(root.children);

I can get updates to work but only without using the collide simulation as seen here:

https://jsfiddle.net/rgdox7g7/1/

node = svg.selectAll('g.node')
    .data(root.children)
    .enter()
    .append('g')
    .attr('id', function(d) { return d.id; })
    .attr('class', 'node')
    .attr('transform', function(d)
    {
        return "translate(" + d.x + "," + d.y + ")";
    });

var nodeUpdate = svg.selectAll('g.node')
    .transition()
    .duration(2000)
    .ease(d3.easeLinear);

var circleUpdate = nodeUpdate.select('circle')
    .attr('r', function(d)
    {
        return d.r;
    });

node.append("circle")
    .attr("r", function(d) { return d.r; })
    .style('fill', function(d) { return color(d.data.name); });

Everything I have tried to mix these two solutions together simply does not work. I have scoured the internet for other examples and nothing I can find is helping. Can someone please help me understand what to do? I never thought D3 would be such a frustration!

stackoverflow: the place where you have to answer your own questions.

here is my working solution: https://jsfiddle.net/zc0fgh6y/

var subscription = null;
var width = 600;
var height = 300;
var maxSpeed = 1000000;
var pack = d3.pack().size([width, height]).padding(0);
var svg = d3.select('svg');
var node = svg.selectAll("g.node");
var root;
var nodes = [];
var first = true;
var scaleFactor = 1.4;

var color = d3.interpolateHcl("#0faac3", "#dd2323");

var forceCollide = d3.forceCollide()
    .strength(.8)
    .radius(function(d)
    {
        return d.r;
    }).iterations(10);

var simulationStart = d3.forceSimulation()
    .force("forceX", d3.forceX(width/2).strength(.04))
    .force("forceY", d3.forceY(height/2).strength(.2))
    .force('collide', forceCollide)
    .on('tick', ticked);

var simulation = d3.forceSimulation()
    .force("forceX", d3.forceX(width/2).strength(.0005))
    .force("forceY", d3.forceY(height/2).strength(.0025))
    .force('collide', forceCollide)
    .on('tick', ticked);

function ticked()
{
    if (node)
    {
        node.attr('transform', function(d)
        {
            return "translate(" + d.x + "," + d.y + ")";
        }).select('circle').attr('r', function(d)
        {
            return d.r;
        });
    }
}

function rand(min, max)
{
    return Math.random() * (max - min) + min;
};


setInterval(function()
{
    var hosts = [];
    for (var i = 0; i < 100; i++)
    {
        hosts.push({name: i, cpu: rand(10,100), speed: rand(0,maxSpeed)});
    }

    root = d3.hierarchy({children: hosts})
        .sum(function(d)
        {
            return d.cpu ? d.cpu : 0;
        });

    var leaves = pack(root).leaves().map(function(item)
    {
        return {
            id: 'node-'+item.data.name,
            name: item.data.name,
            r: item.r * scaleFactor,
            x: width/2,
            y: height/2,
            cpu: item.data.cpu,
            speed: item.data.speed
        };
    });

    for (var i = 0; i < leaves.length; i++)
    {
        if (nodes[i] && nodes[i].id == leaves[i].id)
        {
            var oldR = nodes[i].newR;
            nodes[i].oldR = oldR;
            nodes[i].newR = leaves[i].r;
            nodes[i].cpu = leaves[i].cpu;
            nodes[i].speed = leaves[i].speed;
        }
        else
        {
            nodes[i] = leaves[i];
            //nodes[i].r = 1;
            nodes[i].oldR = 1;//nodes[i].r;
            nodes[i].newR = leaves[i].r;
        }
    }

    if (first)
    {
        first = false;

        node = node.data(nodes, function(d) { return d.id; });

        node = node.enter()
            .append('g')
            .attr('class', 'node');

        node.append("circle")
            .style("fill", 'transparent');

        node.append("text")
            .attr("dy", "0.3em")
            .style('fill', 'transparent')
            .style("text-anchor", "middle")
            .text(function(d)
            {
                return d.name;//.substring(0, d.r / 4);
            });

        // transition in size
        node.transition()
            .ease(d3.easePolyInOut)
            .duration(950)
            .tween('radius', function(d)
            {
                var that = d3.select(this);
                var i = d3.interpolate(1, d.newR);
                return function(t)
                {
                    d.r = i(t);
                    that.attr('r', function(d)
                    {
                        return d.r;
                    });
                    simulationStart.nodes(nodes).alpha(1);
                }
            });

        // fade in text color
        node.select('text')
            .transition()
            .ease(d3.easePolyInOut)
            .duration(950)
            .style('fill', 'white');

        // fade in circle size
        node.select('circle')
            .transition()
            .ease(d3.easePolyInOut)
            .duration(950)
            .style('fill', function(d)
            {
                return color(d.speed / maxSpeed);
            });
    }
    else
    {
        // transition to new size
        node.transition()
            .ease(d3.easeLinear)
            .duration(950)
            .tween('radius', function(d)
            {
                var that = d3.select(this);
                var i = d3.interpolate(d.oldR, d.newR);
                return function(t)
                {
                    d.r = i(t);
                    that.attr('r', function(d)
                    {
                        return d.r;
                    });
                    simulation.nodes(nodes).alpha(1);
                }
            });

        // transition to new color
        node.select('circle')
            .transition()
            .ease(d3.easeLinear)
            .duration(950)
            .style('fill', function(d)
            {
                return color(d.speed / maxSpeed);
            });
    }
}, 1000);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM