2012-11-21 15 views
6

Czy można dostosować prędkość powiększenia, gdy użytkownik przewija i wsuwa za pomocą kółka myszy?Jak zmienić szybkość tłumaczenia i skalować podczas powiększania i pomniejszania w D3 (używając zoom.on i d3.event.translate, d3.event.zoom)?

moim rozumieniu jest to, że zoom.on (https://github.com/mbostock/d3/wiki/Zoom-Behavior#wiki-on) słuchacz produkuje dwa wydarzenia d3.event.translate & d3.event.zoom, które zawierają matryce lub współrzędne że gdy przekazywane do tłumaczenia lub skalę funkcji, umożliwiają przesuwanie i przeskalowanie grafiki.

Ale jak mogę to przyspieszyć, więc jeśli użytkownik przesunie nieco kółko myszy, szybko powiększy lub pomniejszy? Mam dużą wizualizację, która umożliwia użytkownikom szybkie i szybkie powiększanie i pomniejszanie za pomocą kółka myszy. Czy mogę po prostu modyfikować/dodawać argumenty do powyższych zdarzeń i funkcji lub czy muszę tworzyć własne? Mam wrażenie, że niektóre z powyższych są niedokładne/niejednolite pod względem zrozumienia, więc proszę wyjaśnij, jeśli tak.

Bardzo prosty jsfiddle przykład tutaj: http://jsfiddle.net/fiddler86/6jJe6/, z identycznym kodem poniżej:

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

wymyśliłeś to? – kishanio

+1

@kishanio Zaproponowałem rozwiązanie –

Odpowiedz

5

Trzeba wyregulować skalę wewnątrz funkcji z funkcją matematyczną po wybraniu funkcji Ważnym jest, że dla x = 0 y = 0 można użyć pow jest łatwiej w tym przypadku Math.pow(d3.event.scale,.1) drugi parametr robi zoom wolniej, gdy jest mniejszy.

To nie jest dobry pomysł, użyj bardzo skomplikowanej funkcji, ponieważ przeglądarka zwolni się.

Gdy masz nową skalę, musisz przeliczyć tłumaczenie. Nie komplikujesz problemu, w SVG masz rzeczywisty wzrost z this.getBBox().height, ale to nie jest tak, ponieważ jesteś jedną iteracją. Można obliczyć nową wysokość z (originalHeight * scale) i tłumaczyć z (originalHeight - (originalHeight * scale))/2

  • Dobrze origialHeight * skala jest newHeight

  • originalHeight - newHeight jest różnica, a chcesz centrum , trzeba podzielić dla 2, połowa części kwadratu i połowa połówki poniżej.

  • Teraz potrzebujemy wykonać akcję z szerokością. To samo

Kod:

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+ ")");    
    }; 

Zauważ, że mogę umieścić 200 i 300 ustalony, można użyć właściwości, należy użyć stałej ...

Stworzyłem skrzypek: http://jsfiddle.net/t0j5b3e2/

1

Zmodyfikowałem mbostock's drag+zoom example, aby uzyskać szybkość zbliżenia 4x i umieścić ją w jsfiddle. Wyjaśniłem moje myślenie poniżej. To jest moja pierwsza próba odpowiedzi na przepełnienie stosu, proszę bądź miły.

Jak wyjaśniono w odpowiedzi Raúla Martína, można użyć wzoru w ramach funkcji redraw(), aby zmienić współczynnik powiększenia. Musisz wykonać dodatkowe czynności, aby upewnić się, że zachowanie d3 nadal działa poprawnie ze zmodyfikowaną stopą powiększenia.

powiększenia wyśrodkowany na wybranym miejscu (na przykład kursor)
Poprzez zachowanie domyślne d3 skupia powiększenie na wskaźnik myszy, np jeśli masz wskaźnik myszy w lewym górnym rogu obrazu, przybliża się on w lewym górnym rogu, a nie w środku obrazu. Aby uzyskać ten efekt, skaluje obraz, a następnie zmienia translację obrazu tak, aby punkt pod kursorem myszy pozostawał w tym samym miejscu na ekranie. Z tego powodu wartość zoom.translate() zmienia się po przewinięciu koła myszy, nawet jeśli obraz nie wygląda tak, jak porusza się po ekranie.

Po zmianie prędkości powiększenia wartości d3 zoom.translate() nie są poprawne. Wypracować prawidłowe tłumaczenie trzeba znać następujące informacje (ignorowania wartości liczbowe):

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 

formuła wypracować new_translate zastosować do obrazu jest wówczas:

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; 

You można zastosować to do obrazu wraz z nową skalę z:

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

będziesz wtedy musiał aktualizować prev_scale = new_scale i prev_translate = new_translate gotowy do następna iteracja redraw()

Pan bez skalowania zachowanie powiększenia

D3 pozwala na patelni bez skalowania, klikając i przeciągając. Jeśli klikniesz i przeciągniesz, zoom.scale() pozostanie niezmienione, ale zmiany zostaną zmienione na zoom.translate(). Nowa wartość zoom.translate() jest nadal poprawna nawet po zmianie prędkości powiększenia. Musisz jednak wiedzieć, kiedy użyć tej wartości: zoom.translate() i kiedy użyć wartości translate, którą obliczysz, aby powiększyć punkt środkowy.

Możesz sprawdzić, czy patelnia lub zoom działa, sprawdzając, czy prev_scale to to samo, co . Jeśli te dwie wartości są identyczne, wiesz, że ma miejsce patelnia i możesz użyć new_translate = zoom.translate(), aby przesunąć obraz. W przeciwnym razie wiesz, że ma miejsce powiększenie i możesz obliczyć wartość new_translate zgodnie z powyższym opisem. Robię to, dodając funkcję do zdarzenia 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 + ")"); 
}