【问题标题】:Dragging and panning in d3 force layoutd3强制布局中的拖动和平移
【发布时间】:2014-08-29 20:36:05
【问题描述】:

我正在制作一个显示作者关系的力布局图。由于有这么多,我尝试实现缩放和拖动。缩放效果很好(有一个例外),但是当我拖动一个节点时,它也会拖动背景。我尝试按照 Mike Bostock 的指示 herethe 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; });
  }

【问题讨论】:

标签: javascript svg d3.js force-layout


【解决方案1】:

我想我明白了。

我必须结合来自herehere 的说明,这在我链接的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】:

    您可以使用力对象的drag() 方法,而不是创建单独的拖动行为。比如:

    node.call(force.drag);
    

    或者,等价的,

    force.drag(node);
    

    一个完整的例子可以在http://bl.ocks.org/sathomas/a7b0062211af69981ff3获得

    【讨论】:

    • 我的节点下已经有一个node.call(force.drag)。它在技术上有效,但两个拖动事件同时发生;即,每当我拖动节点时,都会拖动节点和背景。
    【解决方案3】:

    这对我有用:

    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】:

      使用您的代码,可以拖动节点,但是当您拖动节点时,其他节点也会移动。我提出这个来停止其余节点,让你完成拖动然后重新生成整个图表

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

      【讨论】:

        猜你喜欢
        • 2012-09-22
        • 1970-01-01
        • 2017-03-02
        • 2013-12-04
        • 1970-01-01
        • 1970-01-01
        • 2015-07-08
        • 1970-01-01
        • 2015-05-27
        相关资源
        最近更新 更多