2016-08-26 8 views
1

Цели: Центр узел в области просмотра следующего поискаd3.js Узел Позиция в Force Directed Graph следующей Поиск

Я создал силы ориентированного граф, который позволяет пользователю искать узел по имени. При поиске узла выбранный узел остается видимым, а все остальные узлы временно уменьшают непрозрачность, чтобы выделить найденный узел. Теперь я также хотел бы центрировать найденный узел в области просмотра. Я попытался использовать множество методов, но был неудачным при переводе всего графика, включая метки узлов. Любая помощь будет принята с благодарностью.

jsfiddle (обратите внимание: поиск автозаполнения отлично работает при запуске из моего сценария, но, кажется, не в состоянии в jsfiddle): https://jsfiddle.net/dereksmith5822/73j63nn0/

<script> 

var width = 900, 
    height = 590; 

var svg = d3.select("body") 
     .append("svg") 
     .attr("width", width) 
     .attr("height", height) 
     .call(d3.behavior.zoom().scaleExtent([0.1,5]).on("zoom", redraw)).on("dblclick.zoom", null) 
     .append('g'); 

//INPUT DATA 
var links = [ 
    {source: 'N1', target: 'N2'}, 
    {source: 'N1', target: 'N3'}, 
    {source: 'N2', target: 'N3'}, 
    {source: 'N3', target: 'N4'}, 
]; 

var nodes = [ 
    {id: 'N1', name: 'A'}, 
    {id: 'N2', name: 'B'}, 
    {id: 'N3', name: 'C'}, 
    {id: 'N4', name: 'D'}, 
]; 

//CONNECTIONS 
var hash_lookup = []; 
nodes.forEach(function(d, i) { 
    hash_lookup[d.id] = d; 
}); 
links.forEach(function(d, i) { 
    d.source = hash_lookup[d.source]; 
    d.target = hash_lookup[d.target]; 
}); 

//FORCE LAYOUT 
var force = d3.layout.force() 
    .size([width, height]) 
    .nodes(d3.values(nodes)) 
    .links(links) 
    .on('tick', tick) 
    .linkDistance(100) 
    .gravity(.15) 
    .friction(.8) 
    .linkStrength(1) 
    .charge(-425) 
    .chargeDistance(600) 
    .start(); 

//LINKS 
var link = svg.selectAll('.link') 
    .data(links) 
    .enter().append('line') 
    .attr('class', 'link'); 

//NODES 
var node = svg.selectAll('.node') 
    .data(force.nodes()) 
    .enter().append('circle') 
    .attr('class', 'node') 
    .attr('r', width * 0.01) 

//LABELS 
var text_center = false; 
var nominal_text_size = 12; 
var max_text_size = 22; 
var nominal_base_node_size = 8; 
var max_base_node_size = 36;  
var size = d3.scale.pow().exponent(1) 
    .domain([1,100]) 
    .range([8,24]); 

var text = svg.selectAll(".text") 
    .data(nodes) 
    .enter().append("text") 
    .attr("dy", ".35em") 
    .style("font-size", nominal_text_size + "px") 

    if (text_center) 
    text.text(function(d) { return d.name; }) 
    .style("text-anchor", "middle"); 

    else 
    text.attr("dx", function(d) {return (size(d.size)|| nominal_base_node_size);}) 
    .text(function(d) { return '\u2002'+d.name; }); 

//ZOOM AND PAN 
function redraw() { 
    svg.attr("transform", 
     "translate(" + d3.event.translate + ")" 
     + " scale(" + d3.event.scale + ")"); 
    } 

var drag = force.drag() 
     .on("dragstart", function(d) { 
     d3.event.sourceEvent.stopPropagation(); 
     }); 

//NODES IN SPACE 
function tick(e) { 

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

    node.attr('cx', function(d) { return d.x; }) 
     .attr('cy', function(d) { return d.y; }) 
     .call(force.drag); 

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

}; 

//AUTOCOMPLETE SEARCH 
var optArray = []; 
for (var i = 0; i < nodes.length - 1; i++) { 
    optArray.push(nodes[i].name); 
} 
optArray = optArray.sort(); 

$(function() { 
    $("#search").autocomplete({ 
     source: optArray 
    }); 
}); 

function searchNode() { 
    var selectedVal = document.getElementById('search').value; 
    if (selectedVal == 'none') {} 
    else { 
     var selected = node.filter(function (d, i) { 
      return d.name != selectedVal; 
     }); 
     var selectedText = text.filter(function (d, i) { 
      return d.name != selectedVal; 
     }); 
     selected.style("opacity", "0"); 
     selectedText.style("opacity", "0"); 
     var link = svg.selectAll(".link") 
      link.style("opacity", "0"); 
     d3.selectAll(".node, .link, .text").transition() 
      .duration(3000) 
      .style("opacity", '1'); 
    } 
} 
    </script> 

ответ

2

Вот идея, как вы можете это сделать:

Сохранить zoom поведение:

var zoom = d3.behavior.zoom().scaleExtent([0.1,5]).on("zoom", redraw); 
var svg = d3.select("body") 
    .append("svg") 
    .attr("width", width) 
    .attr("height", height) 
    .call(zoom).on("dblclick.zoom", null) 
    .append('g'); 

В searchNode функции:

var selectedNode = node 
    .filter(function (d, i) { return d.name == selectedVal; }) 
    .datum(); 
var desiredPosition = { x: 100, y: 100 }; // constants, set to svg center point 
zoom.translate([desiredPosition.x - selectedNode.x, desiredPosition.y - selectedNode.y]); 
zoom.event(svg); 

Этот код не учитывает масштаб масштабирования. Я думаю, вы можете просто получить zoom.scale() и умножить на него выбранные координаты.

+0

Спасибо за эту идею. Код работает, чтобы поместить выбранный узел в центр поля; однако при панорамировании или масштабировании график возвращается к исходным позициям. Изменение «.call (zoom) .on (« dblclick.zoom », null)» приводит к ошибке «Uncaught TypeError: невозможно прочитать свойство« apply »undefined». Вы знаете причину этой ошибки? Благодарю. –

+0

Он работает для меня без ошибок в Firefox 48. Моя вилка вашей скрипки: https://jsfiddle.net/v71d1ha8/ –

+0

Я использую хром. Я разрешил ошибку, но если я либо панорамирую, либо увеличиваю масштаб после того, как узел центрирован, граф возвращается в исходное положение перед центрированием. Вы нашли способ включить панорамирование и масштабирование из новой позиции узла? Спасибо –