最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - D3js force layout destroy and reset - Stack Overflow

programmeradmin4浏览0评论

Based on two D3 examples: Force layout () and clustered force layout (), I managed to built a force layout with a few independent point of gravity to control nodes position on top of the links between nodes.

// Set up map
function map_init(){

    force = d3.layout.force()
        .nodes(nodes)
        .links(links)
        .size([width, height])
        .on("tick", tick);

    svg = d3.select("#map").append("svg")
        .attr("width", width)
        .attr("height", height);

    link = $map.selectAll(".link");
    node = $map.selectAll(".node");

    d3.json("graph.json", function(error, graph) {

        // set up nodes
        for( i = 0; i < graph.nodes.length; i++ ){          
            nodes.push( graph.nodes[i] );
        }

        // position nodes to three different gravity centres based on theme
        for( i = 0; i < nodes.length; i++ ){
            if ( nodes[i].theme == "theme1" ){ 
                nodes[i].cx = 100;
                nodes[i].cy = 100; 
            } else if ( nodes[i].theme == "theme2" ){ 
                nodes[i].cx = 300;
                nodes[i].cy = 300; 
            } else if ( nodes[i].theme == "theme3" ){ 
                nodes[i].cx = 500;
                nodes[i].cy = 500; 
            }   
        }

        // link nodes of the same theme
        theme1_nodes = nodes.filter(function(d){ return (d.theme == "theme1"); });
        theme2_nodes = nodes.filter(function(d){ return (d.theme == "theme2"); });
        theme3_nodes = nodes.filter(function(d){ return (d.theme == "theme3"); });
        for (i = 0; i < theme1_nodes.length-1; i++){
            links.push({ source: theme1_nodes[i], target: theme1_nodes[i+1] });
        }
        for (i = 0; i < theme2_nodes.length-1; i++){
            links.push({ source: theme2_nodes[i], target: theme2_nodes[i+1] });
        }
        for (i = 0; i < theme3_nodes.length-1; i++){
            links.push({ source: theme3_nodes[i], target: theme3_nodes[i+1] });
        }

        start();

    }); 

}

// Start
function start() {

  link = link.data(force.links(), function(d) { return d.source.id + "-" + d.target.id; });
  link.enter()
    .insert("svg:line")
        .attr("class", "link");
  link.exit()
        .remove();

  node = node.data(force.nodes(), function(d) { return d.id; });
  var nodeEnter = node.enter()
          .append("svg:g")
            .attr("class", "node");
        .on("click", map_nodeClick);
  node.exit().remove();

  // Enter node information
  nodeEnter.each(function(d) {
        theTitle = d3.select(this).append("svg:text")
        .attr("font-family", "Helvetica")
            .attr("class", "title")
        .text( d.title );
    });

    // More content to go into each node
    // .
    // .
    // .

  force.start();

}

// Tick
function tick(e) {

    node
      .each(gravity(.2 * e.alpha))
      .attr("cx", function(d) { return d.x; })
      .attr("cy", function(d) { return d.y; })
      .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });

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

}

// Gravity
function gravity(alpha) {

  return function(d) {
    d.y += (d.cy - d.y) * alpha;
    d.x += (d.cx - d.x) * alpha;
  };

}

// Set up when page first loads
map_init();

In order to reset/restart the force layout anytime without reloading the page, I bound the following function to a reset button:

// Remove force layout and data
function map_remove(){

    node.remove();
    link.remove();
    svg.remove();
    nodes = [];
    links = [];

}

// Reset button
$('a#reset').click(function(e){

    e.preventDefault();

    map_remove();
    map_init();

});

This webpage is displayed on a device accessible by group of people. Only loaded once in the morning and stayed running on iPad Safari for 12 hours. Link between nodes ideally changes dynamically based on users input (to be implemented). Apart from the force layout there are other info on the webpage. An option to relaunch/reset the force layout without reloading the page is required.

  1. Is there a built-in method to destroy the D3 force layout and its data?
  2. Currently this works ok as no extra DOM elements were created and no errors found from inspector. However I am not sure how to check if all the D3 objects has been cleared/emptied so no duplicated data was stored/accumulated?
  3. Currently each reset for some reasons pull the nodes closer and closer to the centre of the map. Have I missed out something in the map_remove() function?
  4. Would a plete restart of the D3 force layout improve the performance of the browser at any point? i.e. clearing up the memory for painting the SVG?

Based on two D3 examples: Force layout (http://bl.ocks/mbostock/1095795) and clustered force layout (http://bl.ocks/mbostock/1748247), I managed to built a force layout with a few independent point of gravity to control nodes position on top of the links between nodes.

// Set up map
function map_init(){

    force = d3.layout.force()
        .nodes(nodes)
        .links(links)
        .size([width, height])
        .on("tick", tick);

    svg = d3.select("#map").append("svg")
        .attr("width", width)
        .attr("height", height);

    link = $map.selectAll(".link");
    node = $map.selectAll(".node");

    d3.json("graph.json", function(error, graph) {

        // set up nodes
        for( i = 0; i < graph.nodes.length; i++ ){          
            nodes.push( graph.nodes[i] );
        }

        // position nodes to three different gravity centres based on theme
        for( i = 0; i < nodes.length; i++ ){
            if ( nodes[i].theme == "theme1" ){ 
                nodes[i].cx = 100;
                nodes[i].cy = 100; 
            } else if ( nodes[i].theme == "theme2" ){ 
                nodes[i].cx = 300;
                nodes[i].cy = 300; 
            } else if ( nodes[i].theme == "theme3" ){ 
                nodes[i].cx = 500;
                nodes[i].cy = 500; 
            }   
        }

        // link nodes of the same theme
        theme1_nodes = nodes.filter(function(d){ return (d.theme == "theme1"); });
        theme2_nodes = nodes.filter(function(d){ return (d.theme == "theme2"); });
        theme3_nodes = nodes.filter(function(d){ return (d.theme == "theme3"); });
        for (i = 0; i < theme1_nodes.length-1; i++){
            links.push({ source: theme1_nodes[i], target: theme1_nodes[i+1] });
        }
        for (i = 0; i < theme2_nodes.length-1; i++){
            links.push({ source: theme2_nodes[i], target: theme2_nodes[i+1] });
        }
        for (i = 0; i < theme3_nodes.length-1; i++){
            links.push({ source: theme3_nodes[i], target: theme3_nodes[i+1] });
        }

        start();

    }); 

}

// Start
function start() {

  link = link.data(force.links(), function(d) { return d.source.id + "-" + d.target.id; });
  link.enter()
    .insert("svg:line")
        .attr("class", "link");
  link.exit()
        .remove();

  node = node.data(force.nodes(), function(d) { return d.id; });
  var nodeEnter = node.enter()
          .append("svg:g")
            .attr("class", "node");
        .on("click", map_nodeClick);
  node.exit().remove();

  // Enter node information
  nodeEnter.each(function(d) {
        theTitle = d3.select(this).append("svg:text")
        .attr("font-family", "Helvetica")
            .attr("class", "title")
        .text( d.title );
    });

    // More content to go into each node
    // .
    // .
    // .

  force.start();

}

// Tick
function tick(e) {

    node
      .each(gravity(.2 * e.alpha))
      .attr("cx", function(d) { return d.x; })
      .attr("cy", function(d) { return d.y; })
      .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });

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

}

// Gravity
function gravity(alpha) {

  return function(d) {
    d.y += (d.cy - d.y) * alpha;
    d.x += (d.cx - d.x) * alpha;
  };

}

// Set up when page first loads
map_init();

In order to reset/restart the force layout anytime without reloading the page, I bound the following function to a reset button:

// Remove force layout and data
function map_remove(){

    node.remove();
    link.remove();
    svg.remove();
    nodes = [];
    links = [];

}

// Reset button
$('a#reset').click(function(e){

    e.preventDefault();

    map_remove();
    map_init();

});

This webpage is displayed on a device accessible by group of people. Only loaded once in the morning and stayed running on iPad Safari for 12 hours. Link between nodes ideally changes dynamically based on users input (to be implemented). Apart from the force layout there are other info on the webpage. An option to relaunch/reset the force layout without reloading the page is required.

  1. Is there a built-in method to destroy the D3 force layout and its data?
  2. Currently this works ok as no extra DOM elements were created and no errors found from inspector. However I am not sure how to check if all the D3 objects has been cleared/emptied so no duplicated data was stored/accumulated?
  3. Currently each reset for some reasons pull the nodes closer and closer to the centre of the map. Have I missed out something in the map_remove() function?
  4. Would a plete restart of the D3 force layout improve the performance of the browser at any point? i.e. clearing up the memory for painting the SVG?
Share Improve this question edited Jun 11, 2014 at 9:48 VividD 10.5k8 gold badges66 silver badges112 bronze badges asked Jan 24, 2014 at 16:55 JasonJason 1632 gold badges3 silver badges7 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 9
  1. No. You have to do this manually.
  2. You could have a look at the DOM, but it looks like you're deleting everything.
  3. I'm guessing that this happens because you're not actually deleting the nodes/links from the force layout. At some point, you've given the variables nodes and links to the force layout. Changing what those names point to (i.e. []) doesn't change the reference in the force layout. That is, the data objects are still there and referenced. There are two ways to remove them. You can either modify the nodes and links in place (e.g. with .slice()), or reset them explicitly in the force layout.

    nodes = []; links = []; force.nodes(nodes); force.links(links);

  4. Hard to say without a specific example, but the answer is most likely no. Javascript is garbage collected, so doing it manually shouldn't have an impact.

I did it with:

nodeCircles = {};
node.remove();
link.remove();
svg.clear();
nodes = [];
links = [];

Just put this into a method and then recreate your force and svg. That works great.

发布评论

评论列表(0)

  1. 暂无评论