2013-04-26 24 views
11

Sto usando this nice force layout da Flowingdata.com per creare un diagramma di rete.d3.js forza layout auto zoom/scala dopo il caricamento

enter image description here

enter image description here

enter image description here

mio diagramma mostra attualmente tra 5 e 750 nodi con le loro relazioni. Funziona alla grande con alcune modifiche personalizzate per soddisfare le mie esigenze. Tuttavia, una cosa che non riesco a lavorare. Ho uno viewBox con preserveAspectRatio per adattarlo automaticamente al contenitore in cui si trova. Ma a seconda della quantità di nodi ci sono sempre alcuni nodi attorno ai bordi (principalmente superiore e inferiore) che vengono tagliati. E se ci sono nodi molto piccoli, li mostra nel mezzo con un enorme spazio vuoto attorno ad esso (è un grande contenitore in cui si trova).

C'è un modo per ingrandire o ridimensionare automaticamente il layout per adattarlo automaticamente? In modo che un grande layout venga un po 'ingrandito e ingrandito un piccolo layout. Ho una configurazione dell'evento zoom in modo che lo scorrimento e il panning funzionino come un incantesimo. Ma può farlo automaticamente per adattarsi ai contenuti?

Il codice di avvio 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'); 

risposta

1

È possibile scorrere i nodi; ottenere i valori max e min xey per tutti i nodi e calcolare lo zoom e la traduzione necessari per coprire tutti i risultati all'interno della dimensione SVG.

Ho un pezzo di codice che centra il grafico su un nodo dato; questo potrebbe aiutarti a farti un'idea.

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

Dove w, h sono la larghezza e l'hieight della mia tela SVG, e D il nodo che voglio centrare a.

Invece di centrare a d.x, d.y, è necessario calcolare la media xey. E calcola la tua scala di zoom che farà in modo che la larghezza (e l'altezza) del tuo grafico si adatti alla larghezza (e alle dimensioni) del tuo SVG Scegli lo zoom più grande dei due.

+2

Suona come un buon modo per iniziare. Tuttavia, dove hai ottenuto l'oggetto zoom? Ho provato d3.behavior.zoom e d3.event ma il primo non ha un metodo scale() e quest'ultimo non può chiamare il metodo 'scale' di null. – DaFrenk

1

Il codice dovrebbe essere simile a questo

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

Questo lavoro solo codice per asseX. Perché "cerchio globale" svg RX === RY. Se non fosse per te allora puoi aggiungere la stessa logica per yAxis var startY. Inoltre è necessario regolare le coordinate iniziali considerando cr dei nodi del cerchio.

8

Tutte le altre risposte fino ad oggi richiedono l'accesso ai dati e scorre attraverso di esso in modo che la complessità sia almeno O(nodes). Ho continuato a cercare e ho trovato un modo basato esclusivamente su dimensioni visive già renderizzate, getBBox() che si spera sia O(1). Non importa cosa ci sia dentro o come è strutturato, solo le sue dimensioni e le dimensioni del contenitore genitore.Sono riuscito a sbattere su questo basa su 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

Non intendo disturbarla, ma ho cercato di utilizzare la soluzione per gli ultimi 2 giorni. Il problema è che da getBBox() ottengo la dimensione reale dell'oggetto svg g che è 30x70px invece di ciò che non è visualizzato che sarebbe circa 2000x1700px – Higeath

+0

@Higeath Vedi l'esempio completo dal vivo http://bl.ocks.org/ TWiStErRob/b1c62730e01fe33baa2dea0d0aa29359 – TWiStErRob

+0

So che questo commento sta arrivando un po 'in ritardo, ma la tua risposta è un risparmio di vita totale. Grazie @TWiStErRob! – Terry

1

Ecco due esempi:

Forza layout con zoom automatico (tela):

https://vida.io/documents/pT3RDxrjKHLQ8CQxi

Forza layout con zoom automatico (SVG)

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

La funzione che esegue scala e disegnare per 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

Questa è l'unica soluzione che funziona. Hai salvato la mia tesi Signore :) – Rosiana

Problemi correlati