2016-02-05 6 views
14

Ho zoom e pan che funzionano per l'asse x ma mi piacerebbe aggiungere il panning per l'asse y. Ho provato a usare d3.behavior.zoom e d3.event.translate[1] per ottenere il valore di traduzione y e usarlo, ma il valore di traduzione cambia quando si verifica lo zoom, quindi mentre si trascina il mouse sull'asse y, lo zoom esegue anche panoramiche sull'asse y (in modo non intuitivo).D3: è possibile eseguire lo zoom + eseguire il pan di un asse e eseguire il pan solo sull'altro?

Ho anche provato a utilizzare due istanze d3.behavior.zoom, una per l'asse xe una per l'asse y, ma solo l'ultimo aggiunto viene chiamato sugli eventi di zoom.

Ecco un esempio che funziona per lo zoom e pan nella direzione x che mi piacerebbe anche aggiungere y panning troppo (ma non y zoom):

var x = d3.scale.linear() 
 
    .domain([0, 800]) 
 
    .range([0, 800]); 
 

 
var y = d3.scale.linear() 
 
    .domain([0, 800]) 
 
    .range([0, 800]); 
 

 
var rectangleSelector = d3.select('svg') 
 
    .append('g') 
 
    .selectAll('rect') 
 
    .data([[0, 0], [50, 50], [100, 100]]) 
 
    .enter() 
 
    .append('rect') 
 
    .attr('fill', 'black') 
 
    .attr('x', d => x(d[0])) 
 
    .attr('y', d => y(d[1])) 
 
    .attr('width', d => x(d[0] + 40) - x(d[0])) 
 
    .attr('height', d => y(40)); 
 
    
 
d3.select('svg') 
 
    .call(d3.behavior.zoom().x(x).on('zoom',() => { 
 
    rectangleSelector 
 
     .attr('x', d => x(d[0])) 
 
     .attr('y', d => y(d[1])) 
 
     .attr('width', d => x(d[0] + 40) - x(d[0])) 
 
     .attr('height', d => y(40)); 
 
    }));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script> 
 
<svg width="800" height="800"></svg>

In questo esempio, tento di usare il valore y da d3.event.translate[1] e funziona per il trascinamento, ma il comportamento indesiderato è che, a seconda di dove il mouse dell'utente sta facendo lo zoom, cambia anche il valore di traduzione per l'asse y.

var x = d3.scale.linear() 
 
    .domain([0, 800]) 
 
    .range([0, 800]); 
 

 
var y = d3.scale.linear() 
 
    .domain([0, 800]) 
 
    .range([0, 800]); 
 

 
var rectangleSelector = d3.select('svg') 
 
    .append('g') 
 
    .selectAll('rect') 
 
    .data([[0, 0], [50, 50], [100, 100]]) 
 
    .enter() 
 
    .append('rect') 
 
    .attr('fill', 'black') 
 
    .attr('x', d => x(d[0])) 
 
    .attr('y', d => y(d[1])) 
 
    .attr('width', d => x(d[0] + 40) - x(d[0])) 
 
    .attr('height', d => y(40)); 
 
    
 
d3.select('svg') 
 
    .call(d3.behavior.zoom().x(x).on('zoom',() => { 
 
    var translateY = d3.event.translate[1]; 
 
    
 
    rectangleSelector 
 
     .attr('x', d => x(d[0])) 
 
     .attr('y', d => y(d[1] + translateY)) 
 
     .attr('width', d => x(d[0] + 40) - x(d[0])) 
 
     .attr('height', d => y(40)); 
 
    }));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script> 
 
<svg width="800" height="800"></svg>

+0

'd3.behavior.zoom()' è chainable con 'x()' e 'y()'; Sto solo specificando 'x()' e quindi il comportamento dello zoom cambia solo l'asse x. Ma riporta ancora i valori tradotti per l'asse y, che mi piacerebbe usare per tradurre quell'asse - il problema è che sono influenzati dallo zoom, non solo dal panning. – Beau

+1

Sono in procinto di creare un JSFiddle. – Beau

+0

Forse questo: http://bl.ocks.org/jgbos/9752277 – altocumulus

risposta

10

JSFIDDLE: https://jsfiddle.net/sladav/o8vaecyn/

Per cominciare, il comportamento zoom gestisce sia pan e zoom per l'asse x, e che è gestita da ...

var zoom = d3.behavior.zoom() 
    .x(x) 
    .scaleExtent([1, 10]) 
    .on("zoom", zoomed); 

Così useremo zoom per gestire le cose sull'asse x.

PER L'ASSE Y, per ottenere SOLO il comportamento pan ...

Creare un comportamento di trascinamento separato che cattura il "dy", sposta il dominio y, e riapplica esso.

  1. Aggiungi un comportamento di trascinamento

    var drag = d3.behavior.drag() 
        .on("drag", dragging) 
    
  2. Let dominio della bilancia y essere variabile (modificheremo che, come ci trasciniamo)

    var yPan = 0; 
    var yMin = (-height/2); 
    var yMax = (height/2); 
    
    var y = d3.scale.linear() 
        .domain([yMin, yMax]) 
        .range([height, 0]); 
    
  3. aggiungere una funzione per ridimensionare l'asse Y sull'evento di trascinamento

    function dragging(d) { 
        yPan = d3.event.dy; 
        yMin += yPan; 
        yMax += yPan; 
        y.domain([yMin, yMax]) 
        d3.select(".y.axis").call(yAxis)}; 
    
  4. chiamare la funzione di trascinamento dal vostro elemento SVG

    var svg = d3.select("body").append("svg") 
        .attr("width", width + margin.left + margin.right) 
        .attr("height", height + margin.top + margin.bottom) 
        .append("g") 
         .attr("transform", "translate(" + margin.left + "," + margin.top + ")") 
         .call(zoom) 
         .call(drag); 
    
2

ho capito un modo per fare questo, ma ci si sente come un gigantesco mod:

var lastY = 0; 

var zoom = d3.behavior.zoom().x(x); 

zoom.on('zoom',() => { 
    var translateY; 

    if (d3.event.sourceEvent.type === 'mousemove') { 
    // if it's a drag event then use the value from the drag 
    translateY = d3.event.translate[1]; 
    lastY = translateY; 
    } else { 
    // if it's a wheel event then set the y translation to the last value 
    translateY = lastY; 
    zoom.translate([d3.event.translate[0], translateY]); 
    } 

    // translateY can now be used here as an offset to any drawing like so: 
    rectangleSelector.attr('y', y(d[1] + translateY)); 
}); 
0

È possibile utilizzare

zoom.center
per ottenere il codice di un po 'più pulito, In questo modo:

var lastY = 0; 
 
var x = d3.scale.linear() 
 
    .domain([0, 800]) 
 
    .range([0, 800]); 
 

 
var y = d3.scale.linear() 
 
    .domain([0, 800]) 
 
    .range([0, 800]); 
 

 
var rectangleSelector = d3.select('svg') 
 
    .append('g') 
 
    .selectAll('rect') 
 
    .data([[0, 0], [50, 50], [100, 100]]) 
 
    .enter() 
 
    .append('rect') 
 
    .attr('fill', 'black') 
 
    .attr('x', d => x(d[0])) 
 
    .attr('y', d => y(d[1])) 
 
    .attr('width', d => x(d[0] + 40) - x(d[0])) 
 
    .attr('height', d => y(40)); 
 
    
 
var zoom = d3.behavior.zoom().center([400, 400]).x(x); 
 
zoom.on('zoom',() => { 
 
    var translateY = d3.event.translate[1]; 
 
    zoom.center([400, translateY]); 
 
    rectangleSelector.attr('x', d => x(d[0])) 
 
         .attr('y', d => y(d[1] + translateY)) 
 
         .attr('width', d => x(d[0] + 40) - x(d[0])) 
 
         .attr('height', d => y(40)); 
 
}); 
 

 
d3.select('svg').call(zoom);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script> 
 
<svg width="800" height="800"></svg>

+0

lo zoom iniziale cambia ancora l'asse verticale a volte quando provo questo snippet – Beau

+0

Dipende da ciò su cui si imposta il centro iniziale. Puoi provare a cambiare lo zoom iniziale sull'asse Y a 50 (che nel tuo caso è il centro dell'asse Y iniziale) – Doktorn