【问题标题】:d3 focus on node on clickd3 在点击时关注节点
【发布时间】:2017-01-28 01:05:34
【问题描述】:

我正在尝试实现一个强制布局,其中单击一个节点将能够专注于节点周围的区域。我查看了一些示例,但我收到一条错误消息,上面写着link.bounds is not defined。我认为边界没有为力布局定义,并且适用于这个例子,我从中获取了聚焦功能http://bl.ocks.org/mbostock/9656675

var dx, dy, xy 的值应该是多少?

<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script>

var width = 960,
    height = 500,
    active = d3.select(null);

var zoom = d3.behavior.zoom()
    .translate([0, 0])
    .scale(1)
    .scaleExtent([1, 8])
    .on("zoom", zoomed);    

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

var drag = force.drag()
    .on("dragstart", dragstart);

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

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

var g = svg.append("g");    

d3.json("miserables.json", function(error, graph) {
  if (error) throw error;

  force
      .nodes(graph.nodes)
      .links(graph.links)
      .start();

  link = link.data(graph.links)
    .enter().append("line")
      .attr("class", "link");

  node = node.data(graph.nodes)
    .enter().append("circle")
      .attr("class", "node")
      .attr("r", 12)
      .on("click", clicked)
      .call(drag);
});

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("cx", function(d) { return d.x; })
      .attr("cy", function(d) { return d.y; });
}

function clicked(d){
    if (active.node() === this) return reset();
  active.classed("active", false);
  active = d3.select(this).classed("active", true);

  var bounds = link.bounds(d),
      dx = bounds[1][0] - bounds[0][0],
      dy = bounds[1][1] - bounds[0][1],
      x = (bounds[0][0] + bounds[1][0]) / 2,
      y = (bounds[0][1] + bounds[1][1]) / 2,
      scale = Math.max(1, Math.min(8, 0.9 / Math.max(dx / width, dy / height))),
      translate = [width / 2 - scale * x, height / 2 - scale * y];

  svg.transition()
      .duration(750)
      .call(zoom.translate(translate).scale(scale).event);
} 

function reset() {
  active.classed("active", false);
  active = d3.select(null);

  svg.transition()
      .duration(750)
      .call(zoom.translate([0, 0]).scale(1).event);
}    


function dragstart(d) {
  d3.select(this).classed("fixed", d.fixed = true);
}

function zoomed() {
  g.style("stroke-width", 1.5 / d3.event.scale + "px");
  g.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}    

</script>

【问题讨论】:

    标签: javascript d3.js


    【解决方案1】:

    您可以通过使用单击节点的bbox 重写函数来重新创建强制布局的缩放效果:

    function clicked(d){
      if (active.node() === this) return reset();
      active.classed("active", false);
      active = d3.select(this).classed("active", true);
    
      var bbox = active.node().getBBox(),
          bounds = [[bbox.x, bbox.y],[bbox.x + bbox.width, bbox.y + bbox.height]]; //<-- the bounds from getBBox
    
      var dx = bounds[1][0] - bounds[0][0],
          dy = bounds[1][1] - bounds[0][1],
          x = (bounds[0][0] + bounds[1][0]) / 2,
          y = (bounds[0][1] + bounds[1][1]) / 2,
          scale = Math.max(1, Math.min(8, 0.9 / Math.max(dx / width, dy / height))),
          translate = [width / 2 - scale * x, height / 2 - scale * y];
    
      svg.transition()
          .duration(750)
          .call(zoom.translate(translate).scale(scale).event);
    } 
    

    运行代码:

    <!DOCTYPE html>
    <meta charset="utf-8">
    <body>
    <script src="https://d3js.org/d3.v3.min.js"></script>
    <script>
    
    var width = 960,
        height = 500,
        active = d3.select(null);
    
    var zoom = d3.behavior.zoom()
        .scaleExtent([1, 8])
        .on("zoom", zoomed);    
        
    var force = d3.layout.force()
        .size([width, height])
        .charge(-400)
        .linkDistance(40)
        .on("tick", tick);
    
    var drag = force.drag()
        .on("dragstart", dragstart);
    
    var svg = d3.select("body").append("svg")
        .attr("width", width)
        .attr("height", height)
       // .on("click", reset);
    
    var g = svg.append("g");
    
    var link = g.selectAll(".link"),
        node = g.selectAll(".node");
    
    svg
        .call(zoom) // delete this line to disable free zooming
        .call(zoom.event);
    
    d3.json("https://rawgit.com/d3/d3-plugins/master/graph/data/miserables.json", function(error, graph) {
      if (error) throw error;
      
      force
          .nodes(graph.nodes)
          .links(graph.links)
          .start();
    
      link = link.data(graph.links)
        .enter().append("line")
          .attr("class", "links")
          .style("stroke", "#999");
    
      node = node.data(graph.nodes)
        .enter().append("circle")
          .attr("class", "node")
          .attr("r", 12)
          .on("click", clicked)
          //.call(drag);
    });
    
    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("cx", function(d) { return d.x; })
          .attr("cy", function(d) { return d.y; });
    }
    
    function clicked(d){
      if (active.node() === this) return reset();
      active.classed("active", false);
      active = d3.select(this).classed("active", true);
    
      var bbox = active.node().getBBox(),
          bounds = [[bbox.x, bbox.y],[bbox.x + bbox.width, bbox.y + bbox.height]];
    
      var dx = bounds[1][0] - bounds[0][0],
          dy = bounds[1][1] - bounds[0][1],
          x = (bounds[0][0] + bounds[1][0]) / 2,
          y = (bounds[0][1] + bounds[1][1]) / 2,
          scale = Math.max(1, Math.min(8, 0.9 / Math.max(dx / width, dy / height))),
          translate = [width / 2 - scale * x, height / 2 - scale * y];
    
      svg.transition()
          .duration(750)
          .call(zoom.translate(translate).scale(scale).event);
    } 
    
    function reset() {
      active.classed("active", false);
      active = d3.select(null);
    
      svg.transition()
          .duration(750)
          .call(zoom.translate([0, 0]).scale(1).event);
    }    
    
    
    function dragstart(d) {
      d3.select(this).classed("fixed", d.fixed = true);
    }
    
    function zoomed() {
      console.log(d3.event)
      g.style("stroke-width", 1.5 / d3.event.scale + "px");
      g.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
    }    
    
    </script>

    【讨论】:

    • 非常感谢!完美运行。
    • 我尝试使用不同的 JSON 文件以字符串而不是索引的形式实现相同的源和目标,它抛出了 TypeError u.source.x is undefined。我从另一个问题得知 D3.v3 不支持它,需要使用 v4。但是,对于 v4 zoom.translate 是未定义的。如何解决这个问题?
    • 非常感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-01-13
    • 2016-11-01
    • 2015-11-13
    • 2017-03-11
    • 1970-01-01
    • 2014-11-20
    • 1970-01-01
    相关资源
    最近更新 更多