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


80

D3具有指向布局的力量在这里。有没有一种方法可以向该图添加缩放?目前,我能够捕获鼠标滚轮事件,但不确定如何编写重绘函数本身。有什么建议么?

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

另请参阅此示例,Matt Miller撰写的thisismattmiller.com/blog/add-zoom-slider-to-d3-js。它仅在过程结束时添加“ g”元素。
arivero 2012年

4
有人展示了如何zui53(用于可缩放界面的库),并结合d3js:bl.ocks.org/timelyportfolio/5149102
widged

Answers:


97

更新14/4/4

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

更新2/18/2014

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

原始答案

我根据zoom-pan-transform示例获得了这一效果-您可以在此处看到我的jsFiddle:http : //jsfiddle.net/nrabinowitz/QMKm3/

它比我希望的要复杂一些-您必须嵌套几个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 + ")");
}

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

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


1
@Ogg-我不确定您的意思-jsFiddle只是将结果显示在iFrame中,而不是某种自定义浏览器中,因此您看到的真正的浏览器行为。jsFiddle确实添加了一些东西,例如body标签,因此我建议查看框架源并查看缺少的内容。
nrabinowitz 2011年

2
@EricStob-可能是一个新问题。但是请参阅jsfiddle.net/56RDx/2-这只是按照缩放比例的反比例来缩放字体大小。
nrabinowitz '10 -10-10

1
@ajmartin-请参阅zoom.scaleExtent()
nrabinowitz 2012年

1
使用d3的版本3时,在此示例中,无法拖动单个节点。而是平移整个图形,就好像您没有单击节点一样。这适用于版本2,但我需要v3的功能。有任何想法吗?
Daryl Van Sittert 2014年

1
下面是D3 v3的解决方案:stackoverflow.com/questions/17953106/...
大卫·马克思

18

为什么嵌套<g>的?

下面的代码对我来说很好(只有一个<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 + ")");
}

svg中的所有元素都将附加到该vis元素。


1
可能是因为您可能丢失了“ viewBox”,“ preserveAspectRatio”和“ pointer-events”属性,但仍然可以使用吗?
notan3xit 2013年

@ notan3xit是正确的,不需要viewBox,preserveAspectRatio和指针事件。关键是应用transformation上的属性g元素,没有了上svg元素。
Lekensteyn 2014年

似乎不适用于D3 v3,或者缩放功能仍然有效,但是无法移动单个节点。@nrabinowitz解决方案也存在相同的问题。这是nrabinowitz的小提琴,已更新为使用ahaarnos的解决方案:jsfiddle.net/QMKm3/716,这是同一小提琴,已使用D3v3来说明问题:jsfiddle.net/QMKm3/717
David Marx

将缩放行为添加到SVG元素的完美主意,我不知道您可以做到这一点,因此始终使用恼人的背景矩形。在SVG上添加行为至少在现代版本的Chrome,FF和Opera中有效。
rcijvat 2015年

14

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

首先是主代码。这是@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 + ")");
    }   

现在可以进行平移和缩放,但是将无法拖动节点,因为平移功能将覆盖拖动功能。因此,我们需要这样做:

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

这是@nrabinowitz的小提琴,已修改为使用此更清洁的缩放实现,但说明了D3v3如何中断节点拖动: http //jsfiddle.net/QMKm3/718/

这是与D3v3配合使用的相同提琴:http : //jsfiddle.net/QMKm3/719/


2

我可以在没有第二个“ svg:g”追加的情况下运行我的图表。

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

其余部分相同。


但没有矩形:您无法平移(仅缩放)
user1043144 2013年

0

我得到了带有缩放选项的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 + ")");
}

0

如果要缩放和平移布局而不更改节点大小,请尝试以下操作。您也可以拖动节点而不会发抖。该代码基于原始的力布局示例。至于节点和链接数据,请参考原始样本数据。http://bl.ocks.org/mbostock/4062045

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

您可以在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) + ")";
        });


    };

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.