Есть ли способ увеличить масштаб диаграммы компоновки D3?

D3 имеет силовую схему здесь. Есть ли способ добавить масштабирование на этот график? В настоящее время я смог захватить событие колеса мыши, но я не уверен, как написать функцию перерисовки. Любые предложения?

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

Ответ 1

Обновление 6/4/14

См. также Майк Босток ответьте здесь на изменения в D3 v.3 и соответствующий пример. Я думаю, что это, вероятно, заменит ответ ниже.

Обновление 2/18/2014

Я думаю, что ответ @ahaarnos предпочтительнее, если вы хотите, чтобы весь SVG был включен и масштабировался. Вложенные элементы g в моем ответе ниже действительно необходимы только в том случае, если у вас есть объекты без масштабирования в том же SVG (не в исходном вопросе). Если вы применяете поведение к элементу g, то для обеспечения того, чтобы g получал события указателя, необходим фон rect или аналогичный элемент.

Оригинальный ответ

Я получил эту работу, основанную на примере zoom-pan-transform - здесь вы можете увидеть мою jsFiddle: http://jsfiddle.net/nrabinowitz/QMKm3/

Это было немного сложнее, чем я надеялся - вам нужно вложить несколько элементов g, чтобы заставить его работать, установить атрибут SVG pointer-events на all, а затем добавить фоновый прямоугольник для получения (в противном случае он работает только тогда, когда указатель находится над node или ссылкой). Функция 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, поэтому он также масштабирует ширину штриха, например масштабирование изображения.

Существует еще один пример, который иллюстрирует аналогичную технику.

Ответ 2

Почему вложенные <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.

Ответ 3

Представленные ответы работают в D3 v2, но не в v3. Я синтезировал ответы в виде чистого решения и разрешил проблему v3, используя предоставленный здесь ответ: Почему d3.js v3 разбивает мой график силы при реализации масштабирования, когда v2 нет?

Сначала основной код. Это очищенная версия ответа @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 разбивает node перетаскивание: http://jsfiddle.net/QMKm3/718/

И вот та же скрипка, модифицированная для работы с D3v3: http://jsfiddle.net/QMKm3/719/

Ответ 4

Я получил свой график, чтобы работать без второго "svg: g" append.

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

Остальное одно и то же.

Ответ 5

У меня есть решение для диаграммы направленности 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 + ")");
}

Ответ 6

Если вы хотите изменить масштаб и панорамирование, не меняя node -size, попробуйте ниже. Вы также можете перетаскивать узлы без дрожания. Этот код основан на примере оригинального силового макета. Что касается данных узлов и ссылок, обратитесь к исходным данным образца. http://bl.ocks.org/mbostock/4062045

Обратите внимание на переменные xScale и yScale, функции dragstarted(), dragged() и 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) + ")";
        });


    };