Чи є спосіб збільшити графік розміщення сили 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);

Дивіться також цей приклад thisismattmiller.com/blog/add-zoom-slider-to-d3-js, автор Метт Міллер. Він додає лише елемент "g" в кінці процесу.
arivero

4
хтось показав, як поєднувати zui53 (бібліотека для змінних інтерфейсів) та d3js: bl.ocks.org/timelyportfolio/5149102
розширено

Відповіді:


97

Оновлення 6/4/14

Дивіться також відповідь Майка Бостока тут щодо змін у D3 v.3 та відповідний приклад . Думаю, це, напевно, замінює відповідь нижче.

Оновлення 18.02.2014

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

Оригінальна відповідь

Я працював на основі прикладу zoom-pan-transform - ви можете побачити мій jsFiddle тут: http://jsfiddle.net/nrabinowitz/QMKm3/

Це було дещо складніше, ніж я сподівався - вам потрібно вкласти кілька gелементів, щоб він запрацював, встановити pointer-eventsатрибут SVG 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

2
@EricStob - це може бути новим запитанням. Але див. Jsfiddle.net/56RDx/2 - це просто масштабує розмір шрифту на зворотну шкалу масштабування.
nrabinowitz

1
@ajmartin - див.zoom.scaleExtent()
nrabinowitz

1
При використанні версії 3 d3 у цьому прикладі можливість перетягування окремого вузла не працює. Натомість він обертає весь графік так, ніби ви не натиснули на вузол. Це працює на версії 2, але мені потрібна функція на v3. Будь-які ідеї?
Daryl Van Sittert

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", "serveAspectRatio "та" pointer-events ", і це все одно буде працювати?
notan3xit

@ notan3xit правильно, viewBox ,serveAspectRatio та pointer-events не потрібні. Головне - застосовувати transformationатрибут до gелемента, а не до svgелемента.
Лекенштейн

Здається, не працює з D3 v3, точніше, функція масштабування все ще працює, але втрачається можливість переміщення окремих вузлів. Рішення @nrabinowitz має таку ж проблему. Ось оновлена ​​скрипка nrabinowitz для використання рішення ahaarnos : jsfiddle.net/QMKm3/716, а ось та сама скрипка, оновлена ​​для використання D3v3 для ілюстрації проблеми: jsfiddle.net/QMKm3/717
Девід Маркс,

Ідеальна ідея додати поведінку масштабування до елемента SVG, я не знав, що ви можете це зробити, і тому завжди вдавався до використання надокучливих фонових прямокутників. Додавання поведінки у SVG працює принаймні в сучасних версіях Chrome, FF та Opera.
rcijvat

14

Надані відповіді працюють у 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 ламає перетягування вузла: 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

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

Plz зверніть увагу на змінні xScale та yScale, функції dragstarted (), dragged () та dragended (). Змінено також функційну галочку ().

Результат ви можете побачити на 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) + ")";
        });


    };

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.