2013-09-01 8 views
17

Ich versuche, die Bildschirmposition eines Knotens zu erhalten, nachdem das Layout von d3.behavior.zoom() transformiert wurde, aber ich habe nicht viel Glück. Wie kann ich nach dem Übersetzen und Skalieren des Layouts die tatsächliche Position eines Knotens im Fenster ermitteln?Bildschirmpositionen von D3-Knoten nach der Umwandlung abrufen

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

Jede Anleitung würde geschätzt werden.

BEARBEITEN: 'Knoten' oben ist ein Kraftlayout-Knoten, also werden die x- und y-Eigenschaften von der Simulation gesetzt und bleiben konstant, unabhängig davon, welche Art von Transformation angewendet wird.

BEARBEITEN: Die Strategie, die ich benutze, um das SVG zu transformieren kommt von d3 Zoom-Verhalten, das hier beschrieben wird: 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 + ")"); 
} 

Es ist ziemlich einfach. Das Zoom-Verhalten von d3 liefert Pan- und Zoom-Ereignisse an einen Handler, der die Transformationen über das Attribut transform auf das Container-Element anwendet.

BEARBEITEN: Ich arbeite um das Problem herum, indem ich Mauskoordinaten anstelle von Knotenkoordinaten verwende, da ich an der Knotenposition interessiert bin, wenn der Knoten mit dem Mauszeiger über die Maus bewegt wird. Es ist nicht genau das Verhalten, nach dem ich suche, aber es funktioniert größtenteils und ist besser als nichts.

EDIT: Die Lösung war, die aktuelle Transformationsmatrix des Svg-Elements mit element.getCTM() zu erhalten und sie dann zu verwenden, um die x- und y-Koordinaten in einen bildschirmbezogenen Zustand zu versetzen. Siehe unten.

+0

Ich vermute, die Antwort könnte etwas mit getCTM() und/oder getScreenCTM() des SVG-Elements [Grunddatentypen] tun (http://www.w3.org/TR/SVG/types.html#__svg__SVGLocatable__getCTM). Ich frage mich, ob es Hilfsmethoden gibt, die auf den Matrizen funktionieren, die von diesen Funktionen zurückgegeben werden, wie Inverse (tm) oder etwas in diesem Sinne. –

Antwort

7

Sie können versuchen node.getBBox(), um die Pixelpositionen eines engen Bounding-Box um die Knotenformen zu erhalten, nachdem transform angewendet wurde. Sehen Sie hier für mehr: link.

EDIT:

getBBox nicht ganz so funktioniert, dachte ich. Da das Rechteck in Bezug auf den transformierten Koordinatenraum definiert ist, ist es immer relativ zum übergeordneten <g> und wird daher immer für enthaltene Formen gleich sein.

Es gibt eine weitere Funktion namens element.getBoundingClientRect, die anscheinend recht weit verbreitet ist und ihr Rechteck in Pixelposition relativ zur oberen linken Ecke des Browseransichts-Ports zurückgibt. Das bringt Sie näher an das, was Sie wollen, ohne sich direkt mit der Transformationsmatrix herumschlagen zu müssen.

+0

Danke für den Link. Das Dokument sagt definitiv, dass getBBox() Post-Transformation ist, aber ich bekomme die gleichen Koordinaten für node.x und node.y wie für el.getBBox(). X und el.getBBox(). Y. Nur zu beachten, "Knoten" ist ein Kraft Layout-Knoten, der Teil der Auswahl der Daten ist. –

+0

Welche Transformation wird auf das Element angewendet, wenn getBBox aufgerufen wird? –

+0

Ich habe meinen Beitrag bearbeitet, um Ihre Frage zu beantworten. Ich verwende Translate- und Scale-Transformationen über das Transformationsattribut des Containers. [SVG Geometric Zooming] (http://bl.ocks.org/mbstock/3680999) –

21

Es die Lösung auf meine ursprüngliche Frage scheint sieht dies so etwas wie: (. Aktualisiert Drehung unterstützen transformiert)

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

Alternativ dies auch getan werden kann, mit übersetzen und Skalierungseigenschaften von d3.event (wenn Drehtransformationen sind nicht erforderlich):

// 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: ich die unten Form der Funktion der nützlichste und generisch zu sein gefunden, und es scheint aufzustehen, wo getBoundingClientRect hinfällt. Genauer gesagt ergab getBoundingClientRect ungenaue Ergebnisse, wenn ich versuchte, genaue SVG-Knotenpositionen in einem D3-Force-Layout-Projekt zu erhalten, während die unten angegebene Methode die exakten Zentralkoordinaten des Elementsüber mehrere Browser zurückgab.

(Aktualisiert Rotation Transformationen zu unterstützen.)

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

Die Funktion arbeitet durch die Matrix des DOM-Element-Transformation erhalten, und dann die Matrix Drehung mit, Skalierung und übersetzen Informationen, um die Rückkehr Post-Transformations Koordinaten des gegebenen Knotenobjekts.

+1

Vielen Dank, das hat meinen Tag gemacht, ich suchte stundenlang im Internet nach einer Lösung. –

+1

Hum, Können Sie bestätigen, diese Funktion funktioniert immer noch nicht, wenn Rotationen richtig angewendet werden? Weil Sie nicht ctm.b und ctm.c verwenden, um die Koordinaten zu transformieren. – Overdrivr

+0

Wer weiß, wie man die Rotationstransformationen auf die Funktion getElementCoords() anwendet? – poliu2s