web-dev-qa-db-fra.com

Obtention des positions d'é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 disposition a é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 avoir traduit et mis à l'échelle la disposition?

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: `` nœud '' ci-dessus est un nœud de disposition de force, ses propriétés x et y sont donc 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 vient 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 + ")");
}

C'est assez simple. 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, car je suis intéressé par la position du nœud lorsque le nœud est survolé avec le pointeur de la souris. Ce n'est pas exactement le comportement que je recherche, mais cela fonctionne pour la plupart, et c'est mieux que rien.

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

18
t.888

Vous pouvez essayer node.getBBox() pour obtenir les positions en pixels d'un cadre de délimitation serré autour des formes de nœuds après l'application de transform. Voir ici pour plus: lien .

ÉDITER:

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

Il existe une autre fonction appelée element.getBoundingClientRect qui semble être assez largement prise en charge et renvoie son rectangle en position pixel par rapport au coin supérieur gauche du port de vue du navigateur. Cela pourrait vous rapprocher de ce que vous voulez sans avoir à jouer directement avec la matrice de transformation.

7
Scott Cameron

Il semble que la solution à ma question d'origine ressemble à ceci:

(Mis à jour pour prendre en charge 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

Alternativement, cela peut également être fait en utilisant 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 ci-dessous de la fonction était la plus utile et la plus générique, et elle semble se tenir là où getBoundingClientRect tombe. Plus précisément, lorsque j'essayais d'obtenir des positions de nœud SVG précises dans un projet de mise en page de force D3, getBoundingClientRect a produit des résultats inexacts tandis que la méthode ci-dessous renvoyait les coordonnées centrales exactes de l'élément circle sur plusieurs navigateurs.

(Mis à jour pour prendre en charge 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 la rotation de la matrice, l'échelle et la traduction des informations pour renvoyer les coordonnées post-transformation de l'objet nœud donné.

22
t.888