2013-04-26 5 views
11

Я использую this nice force layout с Flowingdata.com для создания сетевой диаграммы.d3.js force layout авто масштабирование/масштаб после загрузки

enter image description here

enter image description here

enter image description here

Моя схема в настоящее время показывает, между 5 и 750 узлов с их отношениями. Он отлично работает с некоторыми пользовательскими изменениями, соответствующими моим потребностям. Как бы то ни было, я не могу работать. У меня есть viewBox с preserveAspectRatio для автоматической установки контейнера, в котором он находится. Но в зависимости от количества узлов всегда есть некоторые узлы вокруг краев (главным образом верх и buttom), которые обрезаются. И если узлов очень мало, они показывают их посередине с огромным пустым пространством вокруг него (это большой контейнер, в котором он находится).

Есть ли способ автоматического масштабирования или масштабирования макета для автоматической установки? Так что большой макет несколько уменьшился, а малый макет увеличился. У меня есть настройка события масштабирования, поэтому прокрутка и панорамирование работают как шарм. Но может ли он автоматически сделать это, чтобы соответствовать содержимому?

Код d3.js запуска:

 vis = d3.select(selection) 
     .append("svg") 
     .attr("viewBox", "0 0 " + width + " " + height) 
     .attr("preserveAspectRatio", "xMidYMid meet") 
     .attr("pointer-events", "all") 
     .call(d3.behavior.zoom().scaleExtent([.1, 3]) 
         .on("zoom", redraw)).append('g'); 

ответ

1

Вы можете перемещаться по узлам; получить значения max и min x и y для всех узлов и рассчитать необходимый масштаб и перевод, чтобы охватить все ваши данные в пределах размера SVG.

У меня есть фрагмент кода, который центрирует график для данного узла; это может помочь вам получить представление.

zs = zoom.scale() 
zt = zoom.translate(); 
dx = (w/2.0/zs) - d.x; 
dy = (h/2.0/zs) - d.y; 
zoom.translate([dx, dy]); 
zoom.scale(zs); 

Где ж, з имеют ширину и hieight моего SVG холста, и d узел я хочу, чтобы центр.

Вместо центрирования на d.x, d.y вы должны вычислить средние x и y. И вычислите масштаб масштабирования, который сделает ширину (и высоту) вашего графика в соответствии с шириной (и размером) вашего SVG. Увеличьте масштаб изображения.

+2

Звучит как хороший способ, чтобы начать. Однако, где вы получили объект масштабирования? Я пробовал d3.behavior.zoom и d3.event, но первый не имеет метода scale(), и последний говорит, что не может называть метод 'scale' of null. – DaFrenk

1

Ваш код должен быть похож на этот

vis = d3.select(selection) 
     .append("svg") 
     .attr("viewBox", "0 0 " + width + " " + height) 
     .attr("preserveAspectRatio", "xMidYMid meet") 
     .attr("pointer-events", "all") 
     .call(zoomListener) 
     .on("zoom", redraw)); 

    var mainGroup = vis.append('g'); 

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

    var zoomListener = d3.behavior.zoom().on("zoom", zoom); 

    var xArray = YOUR_NODES.map(function(d) { //YOUR_NODES is something like json.nodes after forse.end() 
     return d.x 
    }); 

    var minX = d3.min(xArray); 
    var maxX = d3.max(xArray); 

    var scaleMin = Math.abs(width/(maxX - minX)); 
    var startX = (minX)/scaleMin; 
    var startY = 50/scaleMin; 

    // Same as in the zoom function 
    mainGroup.attr("transform", "translate(" + [startX, startY] + ")scale(" + scaleMin + ")"); 

    // Initialization start param of zoomListener 
    zoomListener.translate([startX, startY]); 
    zoomListener.scale(scaleMin); 
    zoomListener.scaleExtent([scaleMin, 1]) 
    vis.call(zoomListener); 

Этот код работает только для XAxis. Потому что «глобальный круг» svg RX === RY. Если это не было для вас, вы можете добавить такую ​​же логику для yAxis var startY. Также вам нужно отрегулировать начальные координаты с учетом cr узлов круга.

8

Все остальные ответы на сегодняшний день требуют доступа к данным и итерации через него, поэтому сложность составляет не менее O(nodes). Я продолжал искать и нашел способ, который основан исключительно на уже визуализированном размере, getBBox(), который, мы надеемся, O(1). Неважно, что в нем или как оно выложено, просто размер и размер родительского контейнера.Мне удалось расшевелить это на основе http://bl.ocks.org/mbostock/9656675:

var root = // any svg.select(...) that has a single node like a container group by #id 

function lapsedZoomFit(ticks, transitionDuration) { 
    for (var i = ticks || 100; i > 0; --i) force.tick(); 
    force.stop(); 
    zoomFit(transitionDuration); 
} 

function zoomFit(transitionDuration) { 
    var bounds = root.node().getBBox(); 
    var parent = root.node().parentElement; 
    var fullWidth = parent.clientWidth || parent.parentNode.clientWidth, 
     fullHeight = parent.clientHeight || parent.parentNode.clientHeight; 
    var width = bounds.width, 
     height = bounds.height; 
    var midX = bounds.x + width/2, 
     midY = bounds.y + height/2; 
    if (width == 0 || height == 0) return; // nothing to fit 
    var scale = 0.85/Math.max(width/fullWidth, height/fullHeight); 
    var translate = [fullWidth/2 - scale * midX, fullHeight/2 - scale * midY]; 

    console.trace("zoomFit", translate, scale); 

    root 
     .transition() 
     .duration(transitionDuration || 0) // milliseconds 
     .call(zoom.translate(translate).scale(scale).event); 
} 
+0

Я не хочу вас беспокоить, но я пытался использовать ваше решение в течение последних 2 дней. Проблема в том, что из getBBox() я получаю реальный размер объекта svg g, который равен 30x70px вместо того, что не отображается, что будет около 2000x1700px – Higeath

+0

@Higeath Смотрите полный пример в прямом эфире http://bl.ocks.org/ TWiStErRob/b1c62730e01fe33baa2dea0d0aa29359 – TWiStErRob

+0

Я знаю, что этот комментарий идет немного поздно, но ваш ответ - это полная спасательная жизнь. Спасибо @TWiStErRob! – Terry

1

Вот два примера:

Layout Force с автоматическим зумом (холст):

https://vida.io/documents/pT3RDxrjKHLQ8CQxi

Layout Force с автоматическим зумом (SVG)

https://vida.io/documents/9q6B62xQgnZcYYen2

Функция, которая выполняет масштабирование и рисовать для SVG:

function scaleAndDraw() { 
    var xExtent = d3.extent(d3.values(nodes), function(n) { return n.x; }), 
     yExtent = d3.extent(d3.values(nodes), function(n) { return n.y; }); 

    if ((xExtent[1] - xExtent[0]) > config.width) { 
    scaleX = (xExtent[1] - xExtent[0])/config.width; 
    scaleY = (yExtent[1] - yExtent[0])/config.height; 

    scale = 1/Math.max(scaleX, scaleY); 

    translateX = Math.abs(xExtent[0]) * scale; 
    translateY = Math.abs(yExtent[0]) * scale, 
    svg.attr("transform", "translate(" + 
     translateX + "," + translateY + ")" + 
     " scale(" + scale + ")"); 
    } 

    draw(); 
} 
+0

Это единственное решение, которое работает. Вы сохранили мою диссертацию сэра :) – Rosiana

Смежные вопросы