2013-09-01 12 views
17

Próbuję uzyskać pozycję ekranu węzła po tym, jak układ został przekształcony przez d3.behavior.zoom(), ale nie mam dużo szczęścia. W jaki sposób mogę uzyskać rzeczywistą pozycję węzła w oknie po przetłumaczeniu i skalowaniu układu?Pobieranie pozycji ekranu węzłów D3 po transformacji

mouseOver = function(node) { 
    screenX = magic(node.x); // Need a magic function to transform node 
    screenY = magic(node.y); // positions into screen coordinates. 
}; 

Wszelkie wskazówki będą mile widziane.

EDYCJA: "węzeł" powyżej jest węzłem układu sił, więc jego właściwości x i y są ustawiane przez symulację i pozostają stałe po zakończeniu symulacji, niezależnie od tego, jaki rodzaj transformacji zostanie zastosowany.

EDYCJA: Strategia, której używam do transformacji SVG pochodzi z zachowania zoomu d3, które jest tutaj opisane: SVG Geometric Zooming.

var svg = d3.select("body").append("svg") 
    .attr("width", width) 
    .attr("height", height) 
    .append("g") 
    .call(d3.behavior.zoom().scaleExtent([1, 8]).on("zoom", zoom)) 
    .append("g"); 

svg.append("rect") 
    .attr("class", "overlay") 
    .attr("width", width) 
    .attr("height", height); 

svg.selectAll("circle") 
    .data(data) 
    .enter().append("circle") 
    .attr("r", 2.5) 
    .attr("transform", function(d) { return "translate(" + d + ")"; }); 

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

To całkiem proste. Funkcja zoomu d3 dostarcza zdarzenia panoramowania i powiększania do programu obsługi, który stosuje przekształcenia do elementu kontenera za pomocą atrybutu transformacji.

EDYCJA: Zajmuję się problemem, używając współrzędnych myszy zamiast współrzędnych węzła, ponieważ interesuje mnie pozycja węzła, gdy węzeł jest przesuwany wskaźnikiem myszy. Nie jest to zachowanie, którego szukam, ale działa w większej części i jest lepsze niż nic.

EDYCJA: Rozwiązaniem było pobranie bieżącej macierzy transformacji elementu svg za pomocą elementu element.getCTM(), a następnie użycie jej do przesunięcia współrzędnych xiy względem stanu ekranu. Zobacz poniżej.

+0

Podejrzewam, że odpowiedź może wymagać zrobienia czegoś z getCTM() i/lub getScreenCTM() elementu SVG [Podstawowe typy danych] (http://www.w3.org/TR/SVG/types.html#__svg__SVGLocatable__getCTM). Zastanawiam się, czy istnieją metody pomocnicze do działania na macierzy zwróconych przez te funkcje, takie jak Inverse (tm) lub coś podobnego do tego. –

Odpowiedz

7

Możesz spróbować node.getBBox(), aby uzyskać pozycje pikseli wąskiego obwiedni wokół kształtów węzłów po zastosowaniu dowolnego transform. Zobacz tutaj więcej: link.

EDIT:

getBBox nie działa zupełnie tak myślałem. Ponieważ prostokąt jest zdefiniowany w kategoriach transformowanej przestrzeni współrzędnych, zawsze jest on względem rodzica <g> i dlatego zawsze będzie taki sam dla zawartych kształtów.

Istnieje inna funkcja o nazwie element.getBoundingClientRect, która wydaje się dość szeroko obsługiwana i zwraca prostokąt w pikselowej pozycji względem lewego górnego rogu portu przeglądarki. To może zbliżyć cię do tego, czego chcesz, bez potrzeby bałagania bezpośrednio w macierzy transformacji.

+0

Dzięki za link. Dokument zdecydowanie mówi, że getBBox() jest post-transformacją, ale otrzymuję te same współrzędne dla node.x i node.y, co robię dla el.getBBox(). X i el.getBBox(). Należy zauważyć, że "węzeł" jest węzłem układu sił, który jest częścią danych selekcji. –

+0

Co to jest transformacja zastosowana do elementu w momencie wywołania getBBox? –

+0

Edytowałem mój post, próbując odpowiedzieć na twoje pytanie. Stosuję transformaty translacji i skali poprzez atrybut transformacji kontenera. [Powiększenie geometryczne SVG] (http://bl.ocks.org/mbostock/3680999) –

21

Wydaje się, że rozwiązanie mojego pierwotnego pytania wygląda mniej więcej tak: (. Zaktualizowany wspierać obrót przemienia)

// The magic function. 
function getScreenCoords(x, y, ctm) { 
    var xn = ctm.e + x*ctm.a + y*ctm.c; 
    var yn = ctm.f + x*ctm.b + y*ctm.d; 
    return { x: xn, y: yn }; 
} 

var circle = document.getElementById('svgCircle'), 
cx = +circle.getAttribute('cx'), 
cy = +circle.getAttribute('cy'), 
ctm = circle.getCTM(), 
coords = getScreenCoords(cx, cy, ctm); 
console.log(coords.x, coords.y); // shows coords relative to my svg container 

Alternatywnie, może to być również wykonane przy użyciu tłumaczyć i właściwości skala od d3.event (jeśli przekształca obrotu nie są potrzebne)

// This function is called by d3's zoom event. 
function zoom() { 

// The magic function - converts node positions into positions on screen.  
function getScreenCoords(x, y, translate, scale) { 
    var xn = translate[0] + x*scale; 
    var yn = translate[1] + y*scale; 
    return { x: xn, y: yn }; 
} 

// Get element coordinates and transform them to screen coordinates. 
var circle = document.getElementById('svgCircle'); 
cx = +circle.getAttribute('cx'), 
cy = +circle.getAttribute('cy'), 
coords = getScreenCoords(cx, cy, d3.event.translate, d3.event.scale); 
console.log(coords.x, coords.y); // shows coords relative to my svg container 

    // ... 
} 

Edycja: okazało się, że poniżej postać funkcji za najbardziej użyteczny i uniwersalne, i wydaje się stanąć tam, gdzie upadł getBoundingClientRect. Dokładniej, gdy próbowałem uzyskać dokładne pozycje węzłów SVG w projekcie układu sił D3, getBoundingClientRect wygenerowało niedokładne wyniki, podczas gdy poniższa metoda zwróciła dokładne współrzędne środka elementu w wielu przeglądarkach.

(Aktualizacja wspieranie transformacji rotacji.)

// Pass in the element and its pre-transform coords 
function getElementCoords(element, coords) { 
    var ctm = element.getCTM(), 
    x = ctm.e + coords.x*ctm.a + coords.y*ctm.c, 
    y = ctm.f + coords.x*ctm.b + coords.y*ctm.d; 
    return {x: x, y: y}; 
}; 

// Get post-transform coords from the element. 
var circle = document.getElementById('svgCircle'), 
x = +circle.getAttribute('cx'), 
y = +circle.getAttribute('cy'), 
coords = getElementCoords(circle, {x:x, y:y}); 

// Get post-transform coords using a 'node' object. 
// Any object with x,y properties will do. 
var node = ..., // some D3 node or object with x,y properties. 
circle = document.getElementById('svgCircle'), 
coords = getElementCoords(circle, node); 

Funkcja działa przez coraz transformacji macierzy elementu DOM, a następnie za pomocą rotacji matryca, skalować i tłumaczyć informacje powrót post-transform współrzędne danego obiektu węzła.

+1

Dziękuję bardzo za to, że mój dzień, szukałem w Internecie godzinami na rozwiązanie. –

+1

Hum, Mógłbyś potwierdzić, ta funkcja nadal nie działa, gdy obroty są stosowane prawidłowo? Ponieważ nie używasz ctm.b i ctm.c do transformacji współrzędnych. – Overdrivr

+0

Ktoś wie, jak zastosować transformację rotacji do funkcji getElementCoords()? – poliu2s

Powiązane problemy