Javascript 修改D3力有向图中的现有节点,无跳跃过渡

Javascript 修改D3力有向图中的现有节点,无跳跃过渡,javascript,d3.js,d3-force-directed,Javascript,D3.js,D3 Force Directed,我有一个连续的处理数据流,从数据库流到客户端,在客户端我使用D3渲染一个力定向图 经过多次尝试,图表似乎正在更新。基本上,圆半径现在会根据新数据进行更新 但是,每次simulation.nodes(nodes_数据)接收到nodes_数据时,整个图形都会重新初始化,并最终成为跳跃过渡 我尝试过改变不同的力。目标值 这是我用来初始化模拟的函数 function simulation_routine(simulation, nodes_data, links_data, width, height)

我有一个连续的处理数据流,从数据库流到客户端,在客户端我使用D3渲染一个力定向图

经过多次尝试,图表似乎正在更新。基本上,圆半径现在会根据新数据进行更新

但是,每次simulation.nodes(nodes_数据)接收到nodes_数据时,整个图形都会重新初始化,并最终成为跳跃过渡

我尝试过改变不同的力。目标值

这是我用来初始化模拟的函数

function simulation_routine(simulation, nodes_data, links_data, width, height){
    simulation.nodes(nodes_data);

    var link_force =  d3.forceLink(links_data)
        .id(function(d) {
            return d.event_id;
        })
        .strength(function(d){
            prob_value = d.prob_value;
            return 2*prob_value;
        })
        .distance(100);

    var charge_force = d3.forceManyBody()
        .strength(-100);

    var center_force = d3.forceCenter(width / 2, height / 2);

    simulation
        .force("charge_force", charge_force)
        .force("center_force", center_force)
        .force("links",link_force)
    ;

    simulation.alphaTarget(0.3).restart();

    console.log("Restarted Simulation");

    return simulation;
}
这是我用来用新节点\ U数据更新模拟的函数

function simulation_update(simulation, nodes_data, links_data, node, link){

    simulation.nodes(nodes_data);

    var link_force =  d3.forceLink(links_data)
        .id(function(d) {
            return d.event_id;
        })
        .strength(function(d){
            prob_value = d.prob_value;
            return 2*prob_value;
        })
        .distance(100);


    simulation.force("links", link_force);

    simulation.alphaTarget(0.3);

    return simulation;
}

这是节点更新功能

function nodes_update(simulation, nodes_data, links_data){
    var svg = d3.select("svg");
    var g = svg.selectAll(".everything");
    var node = g.selectAll(".nodes").selectAll('g');
    var link = g.selectAll(".links").selectAll('line');
    nodes_routine(node, link, nodes_data, links_data, simulation);
}

function nodes_routine(node, link, nodes_data, links_data, simulation){

    var t = d3.transition().duration(750);

    node = node.data(nodes_data, function(d){return d.event_id;});

    node.exit().remove();

    var newNodes = node.enter().append("g");

    node = newNodes
        .attr('id',function(d){
            return "node_id_"+d.event_id.toString();
        })
        .merge(node)
        ;

    newNodes.append("circle")
        .attr("r", function(d){
            return d.event_radius-0.75;
        })
        .style("fill", function(d, i) {
            return "url(#grad" + i + ")";
        })
        .on('mouseover.fade', fade(0.1))
        .on('mouseover', function(d){
            mouseover_event(d.event_name);
        })
        .on('mouseout.fade', fade(1))
        ;

   node.select("circle")
        .attr("r", function(d) {
            return d.event_radius;
        })
        .style("fill", function(d, i) {
            return "url(#grad" + i + ")";
        });

    newNodes.append("text")
        .text(function(d) {
            return d.event_name.toUpperCase();
        })
        .attr('dy', "0.35em")
        .attr("dx", function(d) {
            return d.event_radius+5||min_base_radius+5;}
        )
        .attr('fill','maroon')
        ;

    newNodes.append("title")
        .text(function(d) { return d.event_name; });

    // Find connected nodes
    const linkedByIndex = {};
    links_data.forEach(d => {
        linkedByIndex[`${d.source.index},${d.target.index}`] = 1;
    });

    //add drag capabilities
    var drag_handler = d3.drag()
        .on("start", drag_start)
        .on("drag", drag_drag)
        .on("end", drag_end);

    drag_handler(node);

    function fade(opacity) {
        return d => {
            node.style('stroke-opacity', function (o) {
                const thisOpacity = isConnected(d, o) ? 1 : opacity;
                this.setAttribute('fill-opacity', thisOpacity);
                return thisOpacity;
            });

            link.style('stroke-opacity', o => (o.source === d || o.target === d ? 1 : opacity));
            link.attr("marker-end", function(o) {
                return opacity === 1 || o.source === d || o.target === d
                ? 'url(#end-arrow)' : 'url(#end-arrow-fade)';
            });

        };
    }


    function isConnected(a, b) {
        return linkedByIndex[`${a.index},${b.index}`] || linkedByIndex[`${b.index},${a.index}`] || a.index === b.index;
    }

    //Drag functions
    //d is the node
    function drag_start(d) {
        if (!d3.event.active) simulation.alphaTarget(0.3).restart();
        d.fx = d.x;
        d.fy = d.y;
    }

    //make sure you can't drag the circle outside the box
    function drag_drag(d) {
        d.fx = d3.event.x;
        d.fy = d3.event.y;
    }

    function drag_end(d) {
        if (!d3.event.active) simulation.alphaTarget(0);
        d.fx = null;
        d.fy = null;
    }

    return node;
}

这是用于更新链接的函数

function links_routine(link, links_data){

    link = link.data(links_data);

    link.exit().remove();

    var newLinks = link.enter().append("line");

    link = newLinks
        .attr("stroke-width", 2)
        .attr("stroke-dasharray", function(d){
            if(d.exists){
                return 0;
            }else{
                return 5;
            }
        })
        .style("stroke", linkColour)
        .attr("marker-end", "url(#end-arrow)")
        .merge(link);

    function linkColour(d){
        return "black";
    }

    return link;
}

function links_update(links_data){
    var svg = d3.select("svg");
    var g = svg.selectAll(".everything");
    var link = g.selectAll(".links").selectAll('line');
    links_routine(link, links_data);
}
我没有收到任何错误消息。这只是一个跳跃的图形转换。我希望图形保留其原始配置,并在现有位置更新节点半径。我想有一个顺利的经历