使用D3.js v4对力导向图进行不稳定的放大

时间:2017-03-18 20:14:14

标签: d3.js graph zooming

我在D3.js(v4)中有这个力导向图:

<!DOCTYPE html>
<html>
<head>
  <script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<style>
    svg text {
         pointer-events: none;     
    }   
</style>
<body>

<script type="text/javascript">
    var  r = 7, w=window.innerWidth, h=window.innerHeight, nodes = [], colors = d3.scaleOrdinal(d3.schemeCategory20c);
    var force = d3.forceSimulation()
            .velocityDecay(0.9)
            .alphaDecay(0)
            .force("charge", d3.forceManyBody())   
            .force("repel", d3.forceManyBody().strength(-140).distanceMax(100).distanceMin(10))
            .force("center", d3.forceCenter(w /2, h/2))
            .force("x", d3.forceX(w / 2))
            .force("y", d3.forceY(h / 2));

    var zoom = d3.zoom()
        .scaleExtent([0.3, 8])
        .on("zoom", zoomed);


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

 d3.json("tiles1.json", function (data) {
        var root = d3.hierarchy(data); 
        var nodes = root.descendants(); 
        var links = root.links(); 
        force.nodes(nodes); 
        force.force("link", d3.forceLink(links).strength(1).distance(50));
          var link = svg.selectAll("line")
              .data(links)
              .enter().insert("line")
              .style("stroke", "#999")
              .style("stroke-width", "1px");
          var nodeElements = svg.selectAll("circle.node")
              .data(nodes)
              .enter().append("circle")
              .attr("r", r)
              .style("fill", function(d) {
                  return colors(d.parent && d.parent.data.name); 
              })
              .style("stroke", "#0b5698")
                .call(d3.drag()
                      .on("start", dragStarted)
                      .on("drag", dragged)
                      .on("end", dragEnded));

        var labels = svg.selectAll(".mytext")
         .data(nodes)
         .enter()
         .append("text")
          .attr("dx", 12)
          .attr("dy", ".35em")
          .text(function(d) { return d.data.name })
                    .style("text-anchor", "middle")
                        .style("fill", "#555")
                        .style("font-family", "Arial")
                        .style("font-size", 7);

      force.on("tick", function(e) {
            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; });
            nodeElements.attr("cx", function(d) { return d.x; })
                .attr("cy", function(d) { return d.y; });
            labels.attr("x", function(d){ return d.x -10; })
                 .attr("y", function (d) {return d.y + 10; });
          });
 });

    function dragStarted(d) {
        d.fx = d.x;
        d.fy = d.y;
    }
    function dragged(d) {
        d.fx = d3.event.x;
        d.fy = d3.event.y;
    }
    function dragEnded(d) {
        d.fx = null;
        d.fy = null;
    }
    function zoomed() {
        svg.attr("transform", d3.event.transform);
    }
</script>
</body>
</html>

tiles1.json是下面的;为了简单起见,我使用的图表只有两个节点。

{    "name": "test", "children": [{
            "name": "test"
        }]
}

一切正常,除了缩放动作:不是将缩放对中在点击的中心,整个图形实际上都转换为左上角,[0,0]原点,即使我的中心原点也是如此图表的中心是屏幕的中心(d3.forceCenter(w/2, h/2))。

我不是D3的大专家,但我知道我在代码中做错了,或者有些东西丢失了,我不明白为什么以及为什么。

我需要你提前帮助。

1 个答案:

答案 0 :(得分:0)

一些事情:

  1. 您调用.call(zoom)的东西是事件处理程序,该元素正在查找要缩放的事件。这不应该是直接的SVG。通常的形式是在SVG顶部放置一个不可见的矩形来捕捉这些事件。
  2. 您无法以您的方式将SVG缩放应用于svg元素。这有点直观,但svg标记是HTML元素,其中的内容是svg个元素。所以你会怎么做?将图形包裹在您应用放大的g中。
  3. 将这两个想法放在一起,最终得到:

    <!DOCTYPE html>
    <html>
    <head>
      <script src="https://d3js.org/d3.v4.min.js"></script>
    </head>
    <style>
        svg text {
             pointer-events: none;     
        }   
    </style>
    <body>
    
    <script type="text/javascript">
        var  r = 7, w=window.innerWidth, h=window.innerHeight, nodes = [], colors = d3.scaleOrdinal(d3.schemeCategory20c);
        var force = d3.forceSimulation()
                .velocityDecay(0.9)
                .alphaDecay(0)
                .force("charge", d3.forceManyBody())   
                .force("repel", d3.forceManyBody().strength(-140).distanceMax(100).distanceMin(10))
                .force("center", d3.forceCenter(w /2, h/2))
                .force("x", d3.forceX(w / 2))
                .force("y", d3.forceY(h / 2));
    
        var zoom = d3.zoom()
            .scaleExtent([0.3, 8])
            .on("zoom", zoomed);
            
        var svg = d3.select("body").append("svg")
            .attr("width", w)
            .attr("height", h)
    
        var g = svg.append("g");
    
        svg.append("rect")
          .attr("width", w)
          .attr("height", h)
          .style("fill", "none")
          .style("pointer-events", "all")
          .call(zoom);   
    
     d3.json("https://jsonblob.com/api/eb8b41b1-0c23-11e7-a0ba-092e370a78bd", function (data) {
            var root = d3.hierarchy(data); 
            var nodes = root.descendants(); 
            var links = root.links(); 
            force.nodes(nodes); 
            force.force("link", d3.forceLink(links).strength(1).distance(50));
              var link = g.selectAll("line")
                  .data(links)
                  .enter().insert("line")
                  .style("stroke", "#999")
                  .style("stroke-width", "1px");
              var nodeElements = g.selectAll("circle.node")
                  .data(nodes)
                  .enter().append("circle")
                  .attr("r", r)
                  .style("fill", function(d) {
                      return colors(d.parent && d.parent.data.name); 
                  })
                  .style("stroke", "#0b5698")
                    .call(d3.drag()
                          .on("start", dragStarted)
                          .on("drag", dragged)
                          .on("end", dragEnded));
    
            var labels = g.selectAll(".mytext")
             .data(nodes)
             .enter()
             .append("text")
              .attr("dx", 12)
              .attr("dy", ".35em")
              .text(function(d) { return d.data.name })
                        .style("text-anchor", "middle")
                            .style("fill", "#555")
                            .style("font-family", "Arial")
                            .style("font-size", 7);
    
          force.on("tick", function(e) {
                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; });
                nodeElements.attr("cx", function(d) { return d.x; })
                    .attr("cy", function(d) { return d.y; });
                labels.attr("x", function(d){ return d.x -10; })
                     .attr("y", function (d) {return d.y + 10; });
              });
     });
    
        function dragStarted(d) {
            d.fx = d.x;
            d.fy = d.y;
        }
        function dragged(d) {
            d.fx = d3.event.x;
            d.fy = d3.event.y;
        }
        function dragEnded(d) {
            d.fx = null;
            d.fy = null;
        }
        function zoomed() {
            g.attr("transform", d3.event.transform);
        }
    </script>
    </body>
    </html>