代码之家  ›  专栏  ›  技术社区  ›  kellyh

在d3力布局中拖动和平移

  •  0
  • kellyh  · 技术社区  · 10 年前

    我正在研究一个显示作者关系的力布局图。由于有这么多,我尝试实现缩放和拖动。缩放效果很好(只有一个例外),但当我拖动节点时,它也会拖动背景。我试着听从Mike Bostock的指示 here the StackOverflow question paired with it ,但它仍然不起作用。我基于图形的大部分代码 this ,效果很好,但由于他使用了旧版本的d3,所以在新版本中他的拖动中断。(我不能只使用旧版本的d3,因为我有一些图中未显示的其他部分,仅适用于较新版本。)

    我认为这个问题与我对SVG对象的分组有关,但我也无法弄清楚我在那里做错了什么。这也带来了一个缩放问题;当我放大或平移时,图例也会移动和放大。如果有一个简单的解决方案可以让它保持静止并在图形上方“悬停”,那就太好了。

    我对编码很陌生,所以我可能犯了非常愚蠢的错误,但如果有任何帮助,我将不胜感激。

    Fiddle.

    var graphData = {
    nodes: [
      {
        id:0,
        name:"Plotinus"
      },
      {
        id:1,
        name:"Iamblichus"
      },
      {
        id:2,
        name:"Porphyry"
      }
    ],
    links: [
      {
        relationship:"Teacher/student",
        source:0,
        target:1
      },
      {
        relationship:"Enemies",
        source:0,
        target:2
      },
      {
        relationship:"Family",
        source:1,
        target:2
      }
     ]
    };
    
    
    var linkColor = d3.scale.category10(); //Sets the color for links
    
    var drag = d3.behavior.drag()
        .on("dragstart", function() { d3.event.sourceEvent.stopPropagation(); })
        .on("drag", function(d) {
            d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y);
        });
    
    var w = 300,
        h = 300;
    
    var vis = d3.select(".graph")
        .append("svg:svg")
        .attr("width", w)
        .attr("height", h)
        .attr("pointer-events", "all")
        .append('svg:g')
        .call(d3.behavior.zoom().on("zoom", redraw))
        .append('svg:g');
    
    vis.append('svg:rect')
        .attr('width', w)
        .attr('height', h)
        .attr('fill', 'rgba(1,1,1,0)');
    
    function redraw() {
        vis.attr("transform","translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")"); } 
    
        var force = d3.layout.force()
            .gravity(.6)
            .charge(-600)
            .linkDistance( 60 )
            .size([w, h]);
    
        var svg = d3.select(".text").append("svg")
            .attr("width", w)
            .attr("height", h);
    
            var link = vis.selectAll("line")
                .data(graphData.links)
                .enter().append("line")
                .style("stroke", function(d) { return linkColor(d.relationship); })
                .style("stroke-width", 1)
                .attr("class", "connector");
    
            var node = vis.selectAll("g.node")
                .data(graphData.nodes)
                .enter().append("svg:g")
                .attr("class","node")
                .call(force.drag);
    
                node.append("svg:circle")
                  .attr("r", 10) //Adjusts size of nodes' radius
                  .style("fill", "#ccc"); 
    
                node.append("svg:text")
                    .attr("text-anchor", "middle") 
                    .attr("fill","black")
                    .style("pointer-events", "none") 
                    .attr("font-size", "9px")
                    .attr("font-weight", "100")
                    .attr("font-family", "sans-serif")
                    .text( function(d) { return d.name;} );
    
    
    // Adds the legend.   
          var legend = vis.selectAll(".legend")
              .data(linkColor.domain().slice().reverse())
            .enter().append("g")
              .attr("class", "legend")
              .attr("transform", function(d, i) { return "translate(-10," + i * 20 + ")"; });
    
          legend.append("rect")
              .attr("x", w - 18)
              .attr("width", 18)
              .attr("height", 18)
              .style("fill", linkColor);
    
          legend.append("text")
              .attr("x", w - 24)
              .attr("y", 9)
              .attr("dy", ".35em")
              .attr("class", "legendText")
              .style("text-anchor", "end")
              .text(function(d) { return d; });
    
        force
            .nodes(graphData.nodes)
            .links(graphData.links)
            .on("tick", tick)
            .start();
    
      function tick() {
        node.attr("cx", function(d) { return d.x; })
            .attr("cy", function(d) { return d.y; })
            .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")";});
    
        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; });
      }
    
    4 回复  |  直到 7 年前
        1
  •  2
  •   Community Radu Dragan    7 年前

    我想我想通了。

    我必须结合 here here ,这在 answer 我联系了。

    我的老办法,从第一个例子开始,看起来是这样的:

    var drag = d3.behavior.drag()
        .on("dragstart", function() { d3.event.sourceEvent.stopPropagation(); })
        .on("drag", function(d) {
            d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y);
        });
    

    问题是我专注于 d3.behavior.drag() 而不是 force.drag ,我想斯蒂芬·托马斯是想告诉我的。应该是这样的:

    //code code code//
    
    function dragstarted(d) {
       d3.event.sourceEvent.stopPropagation();
       d3.select(this).classed("dragging", true);
    }
    
    function dragged(d) {
       d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y);
    }
    
    function dragended(d) {
       d3.select(this).classed("dragging", false);
    }
    
    //code code code//
    
    var drag = force.drag()
    .origin(function(d) { return d; })
    .on("dragstart", dragstarted)
    .on("drag", dragged)
    .on("dragend", dragended);
    
        2
  •  1
  •   Stephen Thomas    10 年前

    您可以使用 drag() 方法,而不是创建单独的拖动行为。类似于:

    node.call(force.drag);
    

    或者等效地,

    force.drag(node);
    

    完整的示例可在 http://bl.ocks.org/sathomas/a7b0062211af69981ff3

        3
  •  0
  •   Tomasz Werszko    9 年前

    以下是对我有用的:

    const zoom = d3.behavior.zoom()
                    .scaleExtent([.1, 10])
                    .on('zoom', zoomed);
    
    const force = d3.layout.force()
                    .(...more stuff...);
    
    const svg = d3.select('.some-parent-div')
                .append('svg')
                .attr('class', 'graph-container')
                .call(zoom);
    
    const mainGroup = svg.append('g');
    
    var node = mainGroup.selectAll('.node');
    
    node.enter()
        .insert('g')
        .attr('class', 'node')
        .call(force.drag)
        .on('mousedown', function(){
            // line below is the key to make it work
            d3.event.stopPropagation();
        })
        .(...more stuff...);
    
    function zoomed(){
        force.stop();
        const canvasTranslate = zoom.translate();
        mainGroup.attr('transform', 'translate('+canvasTranslate[0]+','+canvasTranslate[1]+')scale(' + zoom.scale() + ')');
        force.resume();
    }
    
        4
  •  0
  •   chinglun Mano Mangaldas    8 年前

    使用代码,可以拖动节点,但拖动节点时,其他节点也会移动。我这样做是为了停止其余节点,让您完成拖动,然后重新生成整个图形

    function dragstarted(d) {
       d3.event.sourceEvent.stopPropagation();
       d3.select(this).classed("fixed", d.fixed = true);
    }
    
    function dragged(d) {
       force.stop();
       d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y);
       tick();
    }
    
    function dragended(d) {
       force.resume();
    }