21 votes

Obtenir les positions à l'écran des nœuds D3 après la transformation

J'essaie d'obtenir la position à l'écran d'un nœud après que la mise en page ait été transformée par d3.behavior.zoom() mais je n'ai pas beaucoup de chance. Comment puis-je obtenir la position réelle d'un nœud dans la fenêtre après la transformation et la mise à l'échelle de la présentation ?

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

Tout conseil serait apprécié.

EDIT : le 'nœud' ci-dessus est un nœud de disposition de force, donc ses propriétés x et y sont définies par la simulation et restent constantes après l'arrêt de la simulation, quel que soit le type de transformation appliqué.

EDIT : La stratégie que j'utilise pour transformer le SVG provient du comportement de zoom de d3, qui est décrit ici : Zoom géométrique 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 + ")");
}

Le comportement de zoom de d3 fournit des événements de panoramique et de zoom à un gestionnaire, qui applique les transformations à l'élément conteneur au moyen de l'attribut transform.

EDIT : Je contourne le problème en utilisant les coordonnées de la souris au lieu des coordonnées du nœud, puisque je suis intéressé par la position du nœud lorsque celui-ci est survolé par le pointeur de la souris. Ce n'est pas exactement le comportement que je recherche, mais cela fonctionne en grande partie, et c'est mieux que rien.

EDIT : La solution était de récupérer la matrice de transformation actuelle de l'élément svg avec element.getCTM() et de l'utiliser pour décaler les coordonnées x et y dans un état relatif à l'écran. Voir ci-dessous.

26voto

t.888 Points 2619

Il semble que la solution à ma question initiale ressemble à quelque chose comme ceci :

(Mis à jour pour supporter les transformations de rotation)

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

Vous pouvez également utiliser les propriétés translate et scale de d3.event (si les transformations de rotation ne sont pas nécessaires) :

// 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 : J'ai trouvé que la forme de la fonction ci-dessous était la plus utile et la plus générique, et elle semble tenir la route là où elle est utilisée. getBoundingClientRect tombe. Plus précisément, lorsque j'essayais d'obtenir des positions précises des nœuds SVG dans un projet de mise en page des forces D3, getBoundingClientRect a produit des résultats inexacts, tandis que la méthode ci-dessous a renvoyé la valeur circle les coordonnées exactes du centre de l'élément dans plusieurs navigateurs.

(Mis à jour pour supporter les transformations de rotation)

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

La fonction fonctionne en obtenant la matrice de transformation de l'élément DOM, puis en utilisant les informations de rotation, d'échelle et de translation de la matrice pour renvoyer les coordonnées post-transformation de l'objet nœud donné.

9voto

Scott Cameron Points 2467

Vous pouvez essayer node.getBBox() pour obtenir les positions des pixels d'une boîte de délimitation serrée autour des formes de nœuds après toute transform a été appliqué. Voir ici pour en savoir plus : lien .

EDITAR:

getBBox ne fonctionne pas tout à fait comme je le pensais. Puisque le rectangle est défini en termes d'espace de coordonnées transformé, il est toujours relatif au parent. <g> et sera donc toujours la même pour les formes contenues.

Il existe une autre fonction appelée element.getBoundingClientRect qui semble être largement pris en charge et qui renvoie son rectangle en pixels par rapport à la partie supérieure gauche du port d'affichage du navigateur. Cela pourrait vous permettre de vous rapprocher de ce que vous souhaitez sans avoir à manipuler directement la matrice de transformation.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X