Có cách nào để phóng to biểu đồ bố trí lực D3 không?


80

D3 có bố trí lực lượng hướng ở đây . Có cách nào để thêm tính năng thu phóng vào biểu đồ này không? Hiện tại, tôi đã có thể ghi lại sự kiện con lăn chuột nhưng không thực sự chắc chắn về cách viết chính hàm vẽ lại. Bất kỳ đề xuất?

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

Xem thêm ví dụ này thisismattmiller.com/blog/add-zoom-slider-to-d3-js của Matt Miller. Nó chỉ thêm một phần tử "g" vào cuối quá trình.
arivero

4
ai đó cho thấy làm thế nào để kết hợp zui53 (một thư viện cho các giao diện Zoomable) và d3js: bl.ocks.org/timelyportfolio/5149102
widged

Câu trả lời:


97

Cập nhật 6/4/14

Xem thêm câu trả lời của Mike Bostock tại đây để biết các thay đổi trong D3 v.3 và ví dụ liên quan . Tôi nghĩ rằng điều này có thể thay thế câu trả lời dưới đây.

Cập nhật 18/02/2014

Tôi nghĩ câu trả lời của @ ahaarnos phù hợp hơn nếu bạn muốn toàn bộ SVG xoay và thu phóng. Các gphần tử lồng nhau trong câu trả lời của tôi bên dưới chỉ thực sự cần thiết nếu bạn có các phần tử không thu phóng trong cùng một SVG (không phải trường hợp trong câu hỏi ban đầu). Nếu bạn làm áp dụng hành vi này đến một gyếu tố, sau đó một nền recttố hoặc tương tự là cần thiết để đảm bảo rằng các gnhận sự kiện con trỏ.

Câu trả lời gốc

Tôi đã làm việc này dựa trên ví dụ zoom-pan-biến đổi - bạn có thể xem jsFiddle của tôi tại đây: http://jsfiddle.net/nrabinowitz/QMKm3/

Nó phức tạp hơn một chút so với tôi mong đợi - bạn phải lồng một số gphần tử để nó hoạt động, đặt pointer-eventsthuộc tính của SVG thành all, sau đó thêm một hình chữ nhật nền để nhận các sự kiện con trỏ (nếu không nó chỉ hoạt động khi con trỏ kết thúc một nút hoặc liên kết). Các redrawchức năng là tương đối đơn giản, chỉ cần thiết lập một biến đổi trên trong cùng 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 + ")");
}

Điều này có hiệu quả thay đổi tỷ lệ toàn bộ SVG, vì vậy nó cũng chia tỷ lệ chiều rộng nét vẽ, giống như phóng to hình ảnh.

Có một ví dụ khác minh họa một kỹ thuật tương tự.


1
@Ogg - Tôi không chắc ý bạn ở đây - jsFiddle chỉ trình bày kết quả của bạn trong iFrame, không phải một loại trình duyệt tùy chỉnh nào đó, vì vậy những gì bạn thấy hành vi thực của trình duyệt. jsFiddle có thêm một số thứ, ví dụ như bodythẻ, vì vậy tôi khuyên bạn nên xem nguồn khung và xem bạn đang thiếu những gì.
nrabinowitz

2
@EricStob - đó có thể là một câu hỏi mới. Nhưng hãy xem jsfiddle.net/56RDx/2 - điều này chỉ đơn giản là thay đổi kích thước phông chữ bằng nghịch đảo của tỷ lệ thu phóng.
nrabinowitz

1
@ajmartin - xemzoom.scaleExtent()
nrabinowitz

1
Khi sử dụng phiên bản 3 của d3, khả năng kéo một nút riêng lẻ không hoạt động trong ví dụ này. Thay vào đó, nó xoay toàn bộ đồ thị như thể bạn chưa nhấp vào một nút. Điều này hoạt động trên phiên bản 2 nhưng tôi cần một tính năng trên v3. Có ý kiến ​​gì không?
Daryl Van Sittert

1
Đây là giải pháp cho D3 v3: stackoverflow.com/questions/17953106/…
David Marx

18

Tại sao lồng vào nhau <g>?

Mã dưới đây hoạt động tốt đối với tôi (chỉ một mã <g>, không có màu trắng lớn ngẫu nhiên <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 + ")");
}

Nơi mà tất cả các phần tử trong svg của bạn sau đó được nối vào visphần tử.


1
Có thể là bạn có thể mất các thuộc tính "viewBox", "keepAspectRatio" và "pointer-event" và nó vẫn hoạt động không?
notan3xit

@ notan3xit nói đúng, viewBox, bảo tồnAspectRatio và con trỏ-sự kiện là không cần thiết. Điều quan trọng là áp dụng transformationthuộc tính trên gphần tử, không phải trên svgphần tử.
Lekensteyn

Dường như không hoạt động với D3 v3, hay đúng hơn là tính năng thu phóng vẫn hoạt động nhưng khả năng di chuyển các nút riêng lẻ bị mất. Giải pháp @nrabinowitz cho thấy cùng một vấn đề. Ở đây nrabinowitz' fiddle cập nhật để ahaarnos sử dụng giải pháp: jsfiddle.net/QMKm3/716 và đây là fiddle cùng cập nhật để sử dụng D3v3 để minh họa cho vấn đề: jsfiddle.net/QMKm3/717
David Marx

Ý tưởng hoàn hảo để thêm hành vi thu phóng vào phần tử SVG, tôi không biết bạn có thể làm điều đó và do đó luôn sử dụng các hình chữ nhật nền khó chịu. Việc thêm hành vi trên SVG hoạt động ít nhất trong các phiên bản hiện đại của Chrome, FF và Opera.
rcijvat

14

Các câu trả lời được cung cấp hoạt động trong D3 v2 nhưng không hoạt động trong v3. Tôi đã tổng hợp các câu trả lời thành một giải pháp rõ ràng và giải quyết sự cố v3 bằng câu trả lời được cung cấp ở đây: Tại sao d3.js v3 phá vỡ đồ thị lực của tôi khi thực hiện thu phóng trong khi v2 thì không?

Đầu tiên là mã chính. Đây là phiên bản đã được làm sạch của câu trả lời của @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 + ")");
    }   

Bây giờ bạn có xoay và thu phóng, nhưng bạn sẽ không thể kéo các nút vì chức năng xoay sẽ ghi đè chức năng kéo. Vì vậy, chúng ta cần làm điều này:

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

Đây là @nrabinowitz 'fiddle được sửa đổi để sử dụng triển khai thu phóng rõ ràng hơn này, nhưng minh họa cách D3v3 phá vỡ lực cản của nút: http://jsfiddle.net/QMKm3/718/

Và đây là cùng một fiddle được sửa đổi để hoạt động với D3v3: http://jsfiddle.net/QMKm3/719/


2

Tôi đã nhận được biểu đồ của mình hoạt động mà không có phần phụ "svg: g" thứ hai.

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

Phần còn lại là như nhau.


nhưng không có hình chữ nhật, bạn không thể quay (chỉ phóng to)
user1043144

0

Tôi đã nhận được giải pháp cho đồ thị hướng lực D3 với tùy chọn thu phóng.

    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

Nếu bạn muốn thu phóng và xoay bố cục lực mà không thay đổi kích thước nút, hãy thử bên dưới. Bạn cũng có thể kéo các nút mà không bị run. Mã này dựa trên ví dụ bố trí lực lượng ban đầu. Đối với dữ liệu nút và liên kết, vui lòng tham khảo dữ liệu mẫu gốc. http://bl.ocks.org/mbostock/4062045

Xin lưu ý các biến xScale và yScale, các hàm dragstarted (), drag () và dragended (). Chức năng đánh dấu () cũng đã được thay đổi.

Bạn có thể xem kết quả tại http://steelblue.tistory.com/9 Ngôn ngữ trên trang web là tiếng Hàn. Tuy nhiên, bạn có thể dễ dàng tìm thấy kết quả ở ví dụ thứ ba trên trang.

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


    };

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.