Uzyskiwanie pozycji ekranowych węzłów D3 po transformacji

Próbuję uzyskać pozycję węzła na ekranie po przekształceniu układu przez d3.zachowanie.zoom() ale nie mam dużo szczęścia. Jak Mogę uzyskać rzeczywistą pozycję węzła w oknie po przetłumaczeniu i skalowaniu układu?

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.

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

EDIT: strategia, której używam do przekształcania SVG, pochodzi z zachowania powiększania D3, które jest opisane tutaj: powiększanie geometryczne SVG.

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 dość proste. zachowanie zoom d3 dostarcza zdarzenia panoramowania i powiększania do funkcji obsługi, która stosuje transformacje do elementu kontenera za pomocą atrybutu transform.

EDIT: pracuję nad problemem używając współrzędnych myszy zamiast współrzędnych węzłów, ponieważ interesuje mnie pozycja węzła, gdy węzeł jest najechany wskaźnikiem myszy. To nie jest dokładnie zachowanie jestem po, ale to działa w większości przypadków, i jest lepsze niż nic.

EDIT: rozwiązaniem było uzyskanie aktualnej macierzy transformacji elementu svg z elementem.getCTM (), a następnie użyć go do przesunięcia współrzędnych x i y do stanu względnego ekranu. Patrz poniżej.

Author: t.888, 2013-09-01

2 answers

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

EDIT:

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

Istnieje inna funkcja o nazwie element.getBoundingClientRect , który wydaje się być dość szeroko obsługiwany i zwraca jego prostokąt w pozycji piksela w stosunku do lewego górnego rogu portu widoku przeglądarki. To może zbliżyć cię do tego, czego chcesz, bez konieczności mieszania bezpośrednio z matrycą transformacji.

 8
Author: Scott Cameron,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-02 05:56:11

Wygląda na to, że rozwiązanie mojego pierwotnego pytania wygląda mniej więcej tak:

(zaktualizowano, aby wspierać transformacje rotacyjne.)

// 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żna to również zrobić za pomocą właściwości translate i scale z d3.Zdarzenie (jeżeli transformacja rotacyjna nie jest potrzebna):

// 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

  // ...
}

EDIT: znalazłem poniższy formularz funkcji jako najbardziej użyteczny i ogólny, i wydaje się stać tam, gdzie getBoundingClientRect spada. Dokładniej, kiedy próbowałem uzyskać dokładny węzeł SVG pozycje w projekcie układu wymuszającego D3 getBoundingClientRect dały niedokładne wyniki, podczas gdy poniższa metoda zwracała dokładne współrzędne środka elementu circle w wielu przeglądarkach.

(zaktualizowano, aby wspierać transformacje rotacyjne.)

// 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 poprzez uzyskanie macierzy transformacji elementu DOM, a następnie za pomocą obrotu macierzy, skalowania i tłumaczenia informacji, aby zwrócić współrzędne po transformacji danego obiektu węzła.

 25
Author: t.888,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-03 02:37:50