2

I have been trying to reduce the amount of animation in a d3 project I am working on.

So far I have tried to use various inbuilt functions to inhibit the force of the animation. For example alpha(.1) gravity(1) charge(-1000) although some of these functions don't seem to make a huge difference.

End result: I want to try and eliminate the huge bounce at the start of the animation. Is this possible?

Here is my full js function:

// return data for relationships between database tables
returnTableRelationshipData = function(){

    data = {  
       "nodes":[  
          {  
             "platform":"Source DB",
             "description":"RELATIONSHIPS BETWEEN BUSINESS REFERENCES",
             "ingested":"No",
             "tableId":"RELAC_REFER",
             "level1":"DAEG",
             "level2":"url",
             "nodeId":0
          },
          {  
             "platform":"Source DB",
             // see jsfiddle for full data
       ]
    };

    //find the node index
    function find(f){
      var i = -1
        data.nodes.forEach(function(node, index){
            if(node.nodeId == f)
                i = index;
        });
        return i;
    }

    //set the source and target index
    data.links.forEach(function(d){
        d.source = find(d.source);
        d.target = find(d.target);
    })
    // used to store the number of links between two nodes. 
    var mLinkNum = {};

    // sort links first
    sortLinks();                                

    // set up linkIndex and linkNumer, because it may possible multiple links share the same source and target node
    setLinkIndexAndNum();

    // check that we don't have empty or null values
    checkDataNotEmpty();

    var w = 600,
        h = 500;

    var force = d3.layout.force()
        .nodes(data.nodes)
        .links(data.links)
        .alpha(.1)
        .gravity(1)
        //.distance(150)
        .charge(-1000)
        .size([w, h])
        .start();

    var svg = d3.select(".graphContainer").append("svg:svg")
        .attr("width", w)
        .attr("height", h);

    var path = svg.append("svg:g")
        .selectAll("path")
        .data(force.links())
        .enter().append("line")
        .attr("class", "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;
        });

    var node_drag = d3.behavior.drag()
        .on("dragstart", dragstart)
        .on("drag", dragmove)
        .on("dragend", dragend);

    var circle = svg.append("svg:g")
        .selectAll("circle")
        .data(force.nodes())
        .enter().append("svg:circle")
        .attr("r", 6)
        .call(node_drag);

    var text = svg.append("svg:g")                                
        .selectAll("g")
        .data(force.nodes())
        .enter().append("svg:g");

    text.append("svg:text")
        .text(function(d){ return d.description; });

    /*circle.on("mousedown", function(d) { d.fixed = true; });*/

    force.on("tick", tick);

    function tick() {

        path.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;
        });

        circle.attr("transform", function(d){
            return "translate(" + d.x + "," + d.y + ")";
        });

        text.attr("transform", function(d){
            return "translate(" + d.x + "," + d.y + ")";
        });
    }

    function dragstart(d, i) {
        force.stop(); // stops the force auto positioning before you start dragging
    }

    function dragmove(d, i) {
        d.px += d3.event.dx;
        d.py += d3.event.dy;
        d.x += d3.event.dx;
        d.y += d3.event.dy;
        tick();
    }

    function dragend(d, i) {
        //nodes.fixed = true; // fix all nodes after single drag
        d.fixed = true; // of course set the node to fixed so the force doesn't include the node in its auto positioning stuff
        tick();
        force.resume();
    }

    // sort the links by source, then target
    function sortLinks(){
        if(data.links != null){                         
            data.links.sort(function(a,b){
                if(a.source > b.source){
                    return 1;
                }else if(a.source < b.source){
                    return -1;
                }else{
                    if(a.target > b.target){
                        return 1;
                    }if(a.target < b.target){
                        return -1;
                    }else{
                        return 0;
                    }
                }
            });
        }
    }

    //any links with duplicate source and target get an incremented 'linknum'
    function setLinkIndexAndNum(){                              
        for(var i = 0; i < data.links.length; i++){
            if(i != 0 &&
                data.links[i].source == data.links[i-1].source &&
                data.links[i].target == data.links[i-1].target){
                data.links[i].linkindex = data.links[i-1].linkindex + 1;
            }else{
                data.links[i].linkindex = 1;
            }// save the total number of links between two nodes
            if(mLinkNum[data.links[i].target + "," + data.links[i].source] !== undefined){
                mLinkNum[data.links[i].target + "," + data.links[i].source] = data.links[i].linkindex;
            }else{
                mLinkNum[data.links[i].source + "," + data.links[i].target] = data.links[i].linkindex;
            }
        }
    }

    function checkDataNotEmpty(){
        data.links.forEach(function(link, index, list) {
            if (typeof link.source === 'undefined') {
                console.log('undefined link', data.nodes[link.source]);
            }
            if (typeof link.target === 'undefined') {
                console.log('undefined source', data.nodes[link.target]);
            }
        });
    }
}

returnTableRelationshipData();

Link to jsfiddle

I tried to merge my original code with the SO Question here and linked jsbin here

I managed to post the unminified library to pastebin and then link it to the jsfiddle, for reference here is the full js library: Unminified d3 Library v3

It looks as though the animation functionality starts around line 5807

var d3_ease_default = function() { return d3_identity; };

I have tried commenting out alot of those functions, and the animation appears to run unchanged.

I realise this is not the best question but if anyone has experience of this I would greatly appreciate it.

In addition I am happy to use fixed nodes, but I think they can only be fixed after the force animation has completed.

lharby
  • 3,057
  • 5
  • 24
  • 56

2 Answers2

3

One of the reasons of that huge bounce is that all nodes start the simulation at the same position.

Try to spread them:

data.nodes.forEach(function(node){
    node.x = 200 + Math.random()*200;
    node.y = 150 + Math.random()*200;
}

Here, the magic numbers 200 and 150 are simply w/2 and h/2 minus 100.

Here is the updated fiddle: https://jsfiddle.net/nd8e5m9s/

Gerardo Furtado
  • 100,839
  • 9
  • 121
  • 171
1

you force the d3.layout on dragend that make it force to some point of coordinate and make it bounce, why not remove that

function dragend(d, i) {
    //nodes.fixed = true; // fix all nodes after single drag
    d.fixed = true; // of course set the node to fixed so the force doesn't include the node in its auto positioning stuff
    tick();
    //force.resume();
}
KEKUATAN
  • 948
  • 7
  • 21
  • Thank you. That seems to fix the nodes once they have animated. I would still like to reduce the huge bounce at the start of the animation. – lharby Sep 20 '17 at 11:02
  • Just share....i think d3.layout.force to make it bounce and animated resize path betwen link and node, if you not want to animate maybe you can use d3.forceSimulation() and d3.forceLink, but for this case rought solution is to hide the element before fully loaded, or hard one(but still maybe) is using d3.interpolate but idk how it append on yours case – KEKUATAN Sep 20 '17 at 11:29