2013-10-10 4 views
2

Я пытаюсь построить диаграмму направленной силы в d3.js, на которой есть прослушиватель кликов, который изменяет базовые данные и перерисовывает граф. Я верю, что следую шаблону обновления г-на Бостока, но проблема, с которой я столкнулась, заключается в том, что когда я запускаю обновление, инициированное клиентом, узлы исчезают в нижней части экрана, оставляя ссылки и метки позади.force graph in d3.js - исчезающие узлы

Это обновление, похоже, запускается, обновляет существующие узлы (в этом случае их зеленым), а затем игнорирует разделы «ввод» и «выход» (что является желаемым поведением), а затем обращается к функции tick(), которая отправляет узлы юга.

Я могу заставить это работать, удалив тег «g» на узле и тем самым развязывая метки и узел, что явно нежелательно.

Я не могу не чувствовать, что мне не хватает чего-то очевидного! Или, может быть, я должен придерживаться этого по-другому?

Вот код:

var width = 960, 
    height = 500, 
    links, 
    nodes, 
    root; 

var force = d3.layout.force() 
    .size([width, height]) 
    .charge(-200) 
    .linkDistance(50) 
    .on("tick", tick); 

var svg = d3.select("body").append("svg") 
    .attr("width", width) 
    .attr("height", height); 

var link = svg.selectAll(".link"), 
    node = svg.selectAll(".node"); 

d3.json("test.json", function(json) { 
    root = json; 
    update(); 
}); 

function update() { 
    nodes = root.nodes 
    links = root.links 

    // Restart the force layout. 
    force 
     .nodes(nodes) 
     .links(links) 
     .start(); 

    svg.append("svg:defs").append("marker") 
     .attr("id", "end") 
     .attr("refX", 15) 
     .attr("refY", 2) 
     .attr("markerWidth", 6) 
     .attr("markerHeight", 4) 
     .attr("orient", "auto") 
     .append("svg:path") 
     .attr("d", "M 0,0 V 4 L8,2 Z"); 

    // Update the links… 
    //link = link.data(links, function(d) { return d.target.name; }); 
    link = link.data(links) 

    // Exit any old links. 
    link.exit().remove(); 

    // Enter any new links. 
    link.enter().insert("svg:line", ".node") 
     .attr("class", "link") 
     .attr("marker-end", "url(#end)") 
     .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; }); 

    // Update the nodes… 
    node = svg.selectAll("g").select(".node").data(nodes, function(d) { return d.name; }); 


    node.style("fill", "green") 

    // Exit any old nodes. 
    node.exit().remove(); 

    // Enter any new nodes. 
    node.enter().append("g") 
     .append("svg:circle") 
     .attr("class", "node") 
     .attr("id", function(d) {return "node" + d.index; }) 
     .attr("r", 12) 
     .style("fill", "#BBB") 
     .on("click", click) 
     .call(force.drag); 

    node.append("svg:text") 
     .attr("dx", 16) 
     .attr("dy", ".15em") 
     .attr("class", "nodelabel") 
     .text(function(d) { return d.name }); 
} 

function tick() { 
    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; }); 

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

} 


function click(d) { 
    if (!d3.event.defaultPrevented) { 

    // DO ANYTHING 
    update() 
    } 
} 

и содержание test.json является:

{ 
    "nodes": [ 
    {"name" : "John"}, 
    {"name" : "Alison"}, 
    {"name" : "Phil"}, 
    {"name" : "Jim"}, 
    {"name" : "Jane"}, 
    {"name" : "Mary"}, 
    {"name" : "Joe"} 
    ], 
    "links": [ 
    {"source": 1, "target": 0}, 
    {"source": 2, "target": 0}, 
    {"source": 3, "target": 0}, 
    {"source": 4, "target": 0}, 
    {"source": 5, "target": 1}, 
    {"source": 6, "target": 1} 
    ] 
} 
+0

Силовой макет не позволяет сбросить узлы и ссылки. Чтобы изменить их, установите их один раз, а затем измените основные структуры данных. Не вызывайте 'force.nodes()' или 'force.links()' несколько раз. –

+0

Полезно знать, спасибо. Однако, если я перемещаю этот кусок вне функции update(), у меня все еще есть та же проблема. – alexmloveless

+0

Вы также должны сделать выбор снова внутри 'update()' - ваша переменная 'link' содержит результат выполнения выбора. –

ответ

1

ОК, так что я понял, проблема. Когда я выбирал узлы для обновления я выбор узлов (то есть элементы, которые имели класс «узел»), и они были обновлены:

node = svg.selectAll("g").select(".node").data(nodes, function(d) { return d.name; }); 

Тогда в функции клеща я обновлял эти узлы :

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

так, что совершенно справедливо, однако, узлы инкапсулируются в «г» метки вместе с текстовой меткой, но функция tick() действует только на узле. Исправление было заставить преобразование атрибута в tick() обновить всю группу, а не только в узле:

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

Все теперь работает!

Смежные вопросы