Javascript 有没有办法放大 D3 力布局图?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7871425/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 03:55:02  来源:igfitidea点击:

Is there a way to zoom into a D3 force layout graph?

javascriptjqueryd3.jszoomforce-layout

提问by Legend

D3 has a force directed layout here. Is there a way to add zooming to this graph? Currently, I was able to capture the mouse wheel event but am not really sure how to write the redraw function itself. Any suggestions?

D3在这里有一个力导向布局。有没有办法为这个图形添加缩放?目前,我能够捕获鼠标滚轮事件,但不确定如何编写重绘函数本身。有什么建议?

    var vis = d3.select("#graph")
        .append("svg:svg")
        .call(d3.behavior.zoom().on("zoom", redraw)) // <-- redraw function
        .attr("width", w)
        .attr("height", h);

回答by nrabinowitz

Update 6/4/14

2014 年 6 月 4 日更新

See also Mike Bostock's answer herefor changes in D3 v.3 and the related example. I think this probably supersedes the answer below.

有关D3 v.3 和相关示例中的更改,另请参阅Mike Bostock 的回答。我认为这可能会取代下面的答案。

Update 2/18/2014

2014 年 2 月 18 日更新

I think @ahaarnos's answer is preferable if you want the entire SVG to pan and zoom. The nested gelements in my answer below are really only necessary if you have non-zooming elements in the same SVG (not the case in the original question). If you doapply the behavior to a gelement, then a background rector similar element is required to ensure that the greceives pointer events.

如果您希望整个 SVG 平移和缩放,我认为 @ahaarnos 的答案更可取。g如果您在同一个 SVG 中有非缩放元素(原始问题中不是这种情况),那么下面我的答案中的嵌套元素才是真正必要的。如果您确实将行为应用于g元素,则需要背景rect或类似元素以确保g接收指针事件。

Original Answer

原答案

I got this working based on the zoom-pan-transformexample - you can see my jsFiddle here: http://jsfiddle.net/nrabinowitz/QMKm3/

我根据zoom-pan-transform示例得到了这个工作- 你可以在这里看到我的 jsFiddle:http: //jsfiddle.net/nrabinowitz/QMKm3/

It was a bit more complex than I had hoped - you have to nest several gelements to get it to work, set the SVG's pointer-eventsattribute to all, and then append a background rectangle to receive the pointer events (otherwise it only works when the pointer is over a node or link). The redrawfunction is comparatively simple, just setting a transform on the innermost g:

它比我希望的要复杂一些 - 您必须嵌套几个g元素才能使其工作,将 SVG 的pointer-events属性设置为all,然后附加一个背景矩形以接收指针事件(否则它仅在指针结束时才有效)一个节点或链接)。该redraw功能较为简单,只需设置一个转换的最里面g

var vis = d3.select("#chart")
  .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', 'white');

function redraw() {
  console.log("here", d3.event.translate, d3.event.scale);
  vis.attr("transform",
      "translate(" + d3.event.translate + ")"
      + " scale(" + d3.event.scale + ")");
}

This effectively scales the entire SVG, so it scales stroke width as well, like zooming in on an image.

这有效地缩放了整个 SVG,因此它也会缩放笔触宽度,就像放大图像一样。

There is another examplethat illustrates a similar technique.

还有另一个示例说明了类似的技术。

回答by aaaronic

Why the nested <g>'s?

为什么是嵌套<g>的?

This code below worked well for me (only one <g>, with no random large white <rect>:

下面的这段代码对我来说效果很好(只有一个<g>,没有随机的大白色<rect>

var svg = d3.select("body")
    .append("svg")
      .attr({
        "width": "100%",
        "height": "100%"
      })
      .attr("viewBox", "0 0 " + width + " " + height )
      .attr("preserveAspectRatio", "xMidYMid meet")
      .attr("pointer-events", "all")
    .call(d3.behavior.zoom().on("zoom", redraw));

var vis = svg
    .append('svg:g');

function redraw() {
  vis.attr("transform",
      "translate(" + d3.event.translate + ")"
      + " scale(" + d3.event.scale + ")");
}

Where all the elements in your svg are then appended to the viselement.

然后将 svg 中的所有元素附加到vis元素。

回答by David Marx

The provided answers work in D3 v2 but not in v3. I've synthesized the responses into a clean solution and resolved the v3 issue using the answer provided here: Why does d3.js v3 break my force graph when implementing zooming when v2 doesn't?

提供的答案适用于 D3 v2,但不适用于 v3。我已将响应合成为一个干净的解决方案,并使用此处提供的答案解决了 v3 问题:为什么 d3.js v3 在 v2 没有实现缩放时会破坏我的力图?

First the main code. This is a cleaned up version of @ahaarnos' answer:

先上主代码。这是@ahaarnos 回答的清理版本:

    var svg = d3.select("body")
        .append("svg")
        .attr("width", width)
        .attr("height", height)
            .call(d3.behavior.zoom().on("zoom", redraw))
        .append('g');

    function redraw() {
      svg.attr("transform",
          "translate(" + d3.event.translate + ")"
          + " scale(" + d3.event.scale + ")");
    }   

Now you have pan and zoom, but you won't be able to drag nodes because the pan functionality will override the drag functionality. So we need to do this:

现在您有了平移和缩放功能,但您将无法拖动节点,因为平移功能将覆盖拖动功能。所以我们需要这样做:

var drag = force.stop().drag()
.on("dragstart", function(d) {
    d3.event.sourceEvent.stopPropagation(); // to prevent pan functionality from 
                                            //overriding node drag functionality.
    // put any other 'dragstart' actions here
});

Here's @nrabinowitz' fiddle modified to use this cleaner zoom implementation, but illustrating how D3v3 breaks node drag: http://jsfiddle.net/QMKm3/718/

这是@nrabinowitz' fiddle 修改为使用这种更清晰的缩放实现,但说明了 D3v3 如何打破节点拖动:http: //jsfiddle.net/QMKm3/718/

And here's the same fiddle modified to work with D3v3: http://jsfiddle.net/QMKm3/719/

这是修改为与 D3v3 一起使用的相同小提琴:http: //jsfiddle.net/QMKm3/719/

回答by cem

I got my graph to work without the second "svg:g" append.

我让我的图表在没有第二个“svg:g”附加的情况下工作。

[...].attr("pointer-events", "all")
     .attr("width", width2)
     .attr("height", height2)
     .append('svg:g')
     .call(d3.behavior.zoom().on("zoom", redraw));

The rest is the same.

其余的都是一样的。

回答by Aravind Cheekkallur

I got a solution for D3 force directed graph with zooming option.

我得到了带有缩放选项的 D3 力有向图的解决方案。

    var m = [40, 240, 40, 240],
    width = 960,
    height = 700,
    root;
var svg = d3.select("body").append("svg")
    .attr("class", "svg_container")
    .attr("width", width)
    .attr("height", height)
    .style("overflow", "scroll")
    .style("background-color", "#EEEEEE")
    .append("svg:g")
    .attr("class", "drawarea")
    .append("svg:g")
    .attr("transform", "translate(" + m[3] + "," + m[0] + ")");

//applying zoom in&out for svg
d3.select("svg") 
.call(d3.behavior.zoom()
    .scaleExtent([0.5, 5])
    .on("zoom", zoom));

//zooming 
function zoom() { //zoom in&out function 
    var scale = d3.event.scale,
        translation = d3.event.translate,
        tbound = -height * scale,
        bbound = height * scale,
        lbound = (-width + m[1]) * scale,
        rbound = (width - m[3]) * scale;
    // limit translation to thresholds
    translation = [
        Math.max(Math.min(translation[0], rbound), lbound),
        Math.max(Math.min(translation[1], bbound), tbound)
    ];
    d3.select(".drawarea")
        .attr("transform", "translate(" + translation + ")" +
            " scale(" + scale + ")");
}

回答by seungbum kim

If you want to zoom and pan force layout without changing node-size, try below. You can also drag nodes without trembling. This code is based on original force layout example. As for nodes and links data, please refer to original sample data. http://bl.ocks.org/mbostock/4062045

如果您想在不更改节点大小的情况下缩放和平移强制布局,请尝试以下操作。您还可以毫不颤抖地拖动节点。此代码基于原始力布局示例。节点和链接数据请参考原始样本数据。http://bl.ocks.org/mbostock/4062045

Plz note the variables xScale and yScale, the functions dragstarted(), dragged(), and dragended(). Function tick() was changed as well.

请注意变量 xScale 和 yScale,函数dragstarted()、dragged() 和dragended()。函数 tick() 也已更改。

You can see the result at http://steelblue.tistory.com/9The language on the site is Korean. However you can easily find the result at the third example on the page.

您可以在http://steelblue.tistory.com/9上看到结果。网站上的语言是韩语。但是,您可以在页面上的第三个示例中轻松找到结果。

var graph = {
    "nodes": [
      { "name": "Myriel", "group": 1 },
      { "name": "Napoleon", "group": 1 },
      // ......
      { "name": "Mme.Hucheloup", "group": 8 }
    ],
    "links": [
      { "source": 1, "target": 0, "value": 1 },
      { "source": 2, "target": 0, "value": 8 },
    // .......
      { "source": 76, "target": 58, "value": 1 }
    ]
};
var width = 640,
    height = 400;
 var color = d3.scale.category20();



var xScale = d3.scale.linear()
        .domain([0, width])
         .range([0, width]);

var yScale = d3.scale.linear()
    .domain([0, height])
   .range([0, height]);
var zoomer = d3.behavior.zoom().x(xScale).y(yScale).scaleExtent([0.1, 8]).on("zoom", zoom);
function zoom() {

    tick(); 
};

var drag = d3.behavior.drag()
        .origin(function (d) { return d; })
         .on("dragstart", dragstarted)
        .on("drag", dragged)
        .on("dragend", dragended);

function dragstarted(d) {
    d3.event.sourceEvent.stopPropagation();

    d.fixed |= 2;         
}
function dragged(d) {

    var mouse = d3.mouse(svg.node());
    d.x = xScale.invert(mouse[0]);
    d.y = yScale.invert(mouse[1]);
    d.px = d.x;         
    d.py = d.y;
    force.resume();
}

function dragended(d) {

    d.fixed &= ~6;           }

var force = d3.layout.force()
    .charge(-120)
    .linkDistance(30)
    .size([width, height]);

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

svg.call(zoomer);

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

    var link = svg.selectAll(".link")
        .data(graph.links)
      .enter().append("line")
        .attr("class", "link")
        .style("stroke-width", function (d) { return Math.sqrt(d.value); });

    var node = svg.selectAll(".node")
        .data(graph.nodes)
      .enter().append("circle")
        .attr("class", "node")
        .attr("r", 5)
        .style("fill", function (d) { return color(d.group); })
        .call(drag);

    node.append("title")
        .text(function (d) { return d.name; });

    force.on("tick",tick);

function tick(){            
        link.attr("x1", function (d) { return  xScale(d.source.x); })
            .attr("y1", function (d) { return yScale(d.source.y);  })
            .attr("x2", function (d) { return xScale(d.target.x); })
            .attr("y2", function (d) { return yScale(d.target.y); });

        node.attr("transform", function (d) {
            return "translate(" + xScale(d.x) + "," + yScale(d.y) + ")";
        });


    };