d3.js搜索后的节点位置强制定向图

时间:2016-08-26 21:12:24

标签: javascript d3.js d3-force-directed

目标:在搜索后将查看区域中的节点居中

我创建了一个强制定向图,使用户可以按名称搜索节点。搜索节点时,所选节点保持可见,而所有其他节点暂时降低不透明度以突出显示搜索到的节点。现在,我还希望将搜索到的节点集中在查看区域中。我尝试了很多方法,但是在翻译包括节点标签在内的整个图表方面都没有成功。任何帮助将不胜感激。

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>

1 个答案:

答案 0 :(得分: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()并乘以selectedNode坐标。