2012-11-21 6 views
6

È possibile regolare la velocità dello zoom quando l'utente scorre dentro e fuori usando la rotellina del mouse?Come modificare la velocità di traslazione e ridimensionamento durante l'ingrandimento e la riduzione in D3 (utilizzando zoom.on & d3.event.translate, d3.event.zoom)?

Mi risulta che lo zoom.on (https://github.com/mbostock/d3/wiki/Zoom-Behavior#wiki-on) ascoltatore produce i due eventi d3.event.translate & d3.event.zoom, che contengono matrici o coordinate che quando passato alle funzioni tradurre o scala, consentire panning e riscalaggio della grafica.

Ma come faccio a velocizzare questo, in modo che se l'utente muove la sua rotellina del mouse di un po ', si ingrandisce rapidamente dentro o fuori? Ho una grande visualizzazione che voglio consentire agli utenti di ingrandire e ridurre rapidamente con la rotellina del mouse. Posso semplicemente modificare/aggiungere argomenti agli eventi e funzioni sopra esistenti o devo creare il mio? Ho la sensazione che alcuni di questi siano inaccurati/frammentari in termini di comprensione, quindi per favore spiegatemi se è così.

esempio molto semplice jsfiddle qui: http://jsfiddle.net/fiddler86/6jJe6/, con il codice identico sotto:

var svg = d3.select("body").append("svg:svg") 
     .attr("width", 1000) 
     .attr("height", 2000)  
     .append("svg:g") 
      .call(d3.behavior.zoom().on("zoom", redraw)) 
     .append("svg:g"); 

svg.append("svg:rect") 
.attr("width", 200) 
.attr("height", 300) 
.attr("fill", 'green'); 

function redraw() { 
    svg.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")"); 
};  
+0

hai capito? – kishanio

+1

@kishanio Ho proposto una soluzione –

risposta

5

È necessario regolare la scala all'interno della funzione con una funzione matematica quando si seleziona la funzione l'importante è che per x = 0 la y = 0 puoi usare pow è più facile in questo caso Math.pow(d3.event.scale,.1) il secondo parametro fa lo zoom più lentamente quando è più piccolo.

Non è una buona idea utilizzare una funzione molto complicata perché il browser diventa lento.

Quando si dispone della nuova scala, è necessario ricalcolare la traduzione. Non complichi il problema, in SVG hai l'altezza effettiva con this.getBBox().height ok, ma non è esattamente perché sei un'iterazione dietro. Si potrebbe calcolare la nuova altezza con (originalHeight * scale) e la traduce con (originalHeight - (originalHeight * scale))/2

  • Bene origialHeight * scala è il newheight

  • L'originalHeight - newheight è la differenza, e si desidera il centro , è necessario dividere per 2, la metà del quadrato e la metà di in basso.

  • Ora è necessario eseguire l'azione con la larghezza. E 'lo stesso

Il codice:

var svg = d3.select("body").append("svg:svg") 
       .attr("width", 1000) 
       .attr("height", 2000)  
       .append("svg:g") 
        .call(d3.behavior.zoom().on("zoom", redraw)) 
       .append("svg:g"); 

    svg.append("svg:rect") 
     .attr("width", 200) 
     .attr("height", 300) 
     .attr("fill", 'green'); 

    function redraw() { 
     var velocity = 1/10; 
     var scale = Math.pow(d3.event.scale,velocity); 
     var translateY = (300 - (300 * scale))/2; 
     var translateX = (200 - (200 * scale))/2; 

     svg.attr("transform", "translate(" + [translateX,translateY] + ")" + " scale(" +scale+ ")");    
    }; 

Nota che ho messo il 200 e 300 hardcoded, è possibile utilizzare una proprietà, uso costante ...

ho creato un violinista: http://jsfiddle.net/t0j5b3e2/

1

Ho modificato lo drag+zoom example di mbostock per avere una velocità di zoom 4x e inserirla in un jsfiddle. Ho spiegato il mio pensiero qui sotto. Questo è il mio primo tentativo di una risposta di overflow dello stack, per favore sii gentile.

Come spiegato nella risposta di Raúl Martín, è possibile utilizzare una formula all'interno della funzione redraw() per modificare la velocità dello zoom. È necessario eseguire alcuni passaggi aggiuntivi per assicurarsi che il comportamento di d3 funzioni ancora correttamente con la velocità di zoom modificata.

Zoom centrata su un punto scelto (ad esempio cursore)
Per comportamento predefinito d3 concentra lo zoom il puntatore del mouse, ad esempio se hai il puntatore del mouse nella parte in alto a sinistra dell'immagine, ingrandisce in alto a sinistra anziché al centro dell'immagine. Per ottenere questo effetto, ridimensiona l'immagine e quindi cambia anche la traduzione dell'immagine in modo che il punto sotto il cursore del mouse rimanga nella stessa posizione sullo schermo. Questo è il motivo per cui il valore di zoom.translate() cambia quando si fa scorrere la rotellina del mouse anche se l'immagine non sembra spostarsi sullo schermo.

Se si modifica la velocità dello zoom, i valori di d3 zoom.translate() non sono più corretti. Per calcolare la traduzione corretta è necessario conoscere le seguenti informazioni (ignorare i valori numerici):

var prev_translate = [100,100] // x, y translation of the image in last redraw 
var prev_scale = 0.1   // Scale applied to the image last redraw 
var new_scale = 0.4   // The new scale being applied 
var zoom_cp = [150, 150]  // The zoom "center point" e.g. mouse pointer 

La formula per calcolare il new_translate da applicare all'immagine viene poi:

new_translate[0] = zoom_cp[0] - (zoom_cp[0] - prev_translate[0]) 
    * new_scale/prev_scale; 
new_translate[1] = zoom_cp[1] - (zoom_cp[1] - prev_translate[1]) 
    * new_scale/prev_scale; 

È può applicare questo all'immagine con il vostro nuova scala con:

svg.attr("transform", "translate(" + new_translate + ")scale(" + new_scale + ")"); 

Avrai quindi aggiornare prev_scale = new_scale e prev_translate = new_translate pronto per la prossima iterazione del redraw()

Pan senza ridimensionamento comportamento zoom

la D3 permette di padella senza ridimensionamento cliccando e trascinando. Se si fa clic e si trascina, quindi zoom.scale() rimane lo stesso ma le modifiche zoom.translate(). Il nuovo valore zoom.translate() è ancora corretto anche dopo aver modificato la velocità dello zoom. Tuttavia, è necessario sapere quando utilizzare questo valore zoom.translate() e quando utilizzare il valore di traduzione calcolato per lo zoom su un punto centrale.

È possibile determinare se una panoramica o uno zoom si verificano osservando se prev_scale corrisponde a new scale. Se i due valori sono identici, sai che è in corso una panoramica e puoi utilizzare new_translate = zoom.translate() per spostare l'immagine. Altrimenti, sai che è in corso uno zoom ed è possibile calcolare il valore new_translate come descritto sopra. Lo faccio aggiungendo una funzione all'evento zoomstart.

var zoom_type = "?"; 
var scale_grad = 4; // Zoom speed multiple 
var intercept = 1 * (1 - scale_grad) 

var svg = d3.select("body").append("svg:svg") 
      .attr("width", 1000) 
      .attr("height", 2000)  
      .append("svg:g") 
       .call(d3.behavior.zoom() 
         .on("zoom", redraw) 
         .on("zoomstart", zoomstarted)) 
      .append("svg:g"); 

function zoomstarted() { 
    zoom_type = "?"; 
} 

function redraw() { 
    var scale = d3.event.scale; 

    // Use a linear scale, don't let it go below the minimum scale 
    // extent 
    var new_scale = Math.max(scale_grad * scale + intercept, 
         scale_extent[0]); 

    // If hit the minimum scale extent then stop d3 zoom from 
    // going any further 
    if (new_scale == scale_extent[0]) { 
     zoom.scale((scale_extent[0] - intercept)/scale_grad); 
    } 

    // Set up zoom_type if have just started 
    // If the scale hasn't changed then a pure translation is 
    // taking place, otherwise it is a scale 
    if (zoom_type == "?") { 
     if (new_scale == prev_scale) { 
      zoom_type = "translate" 
     } else { 
      zoom_type = "scale" 
     } 
    } 

    // zoom_cp is the static point during the zoom, set as 
    // mouse pointer position (you need to define a listener to track) 
    var new_translate = [0, 0]; 
    zoom_cp = [mouse_x, mouse_y]; 

    // If the event is a translate just apply d3 translate 
    // Otherwise calculate what translation is required to 
    // keep the zoom center point static 
    if (zoom_type == "translate") { 
     new_translate = d3.event.translate 
    } else if (zoom_type == "scale") { 
     new_translate[0] = zoom_cp[0] 
      - (zoom_cp[0] - prev_translate[0]) * new_scale/prev_scale; 
     new_translate[1] = zoom_cp[1] 
      - (zoom_cp[1] - prev_translate[1]) * new_scale/prev_scale; 
} 

     // Update the variables that track the last iteration of the 
     // zoom 
     prev_translate = new_translate; 
     prev_scale = new_scale; 
     zoom.translate(new_translate); 

     // Apply scale and translate behaviour 
     svg.attr("transform", "translate(" + new_translate + 
      ")scale(" + new_scale + ")"); 
}