tutorial d3js d3.js position zoom transform

d3.js - tutorial - d3js v4



Obtención de posiciones de pantalla de nodos D3 después de la transformación (2)

Parece que la solución a mi pregunta original es algo como esto:

(Actualizado para soportar transformaciones de rotación).

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

Alternativamente, esto también se puede hacer usando las propiedades de traslación y escala de d3.event (si no se necesitan transformaciones de rotación):

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

EDITAR: Encontré que la siguiente forma de la función es la más útil y genérica, y parece que está de pie donde se cae getBoundingClientRect . Más específicamente, cuando intentaba obtener posiciones precisas de nodos SVG en un proyecto de diseño de fuerza D3, getBoundingClientRect produjo resultados inexactos mientras que el método a continuación devolvió las coordenadas del centro exactas del elemento del circle en varios navegadores.

(Actualizado para soportar transformaciones de rotación).

// 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 función funciona obteniendo la matriz de transformación del elemento DOM y luego utilizando la rotación de la matriz, la escala y la información de traducción para devolver las coordenadas posteriores a la transformación del objeto de nodo dado.

Estoy intentando obtener la posición de pantalla de un nodo después de que el diseño haya sido transformado por d3.behavior.zoom () pero no estoy teniendo mucha suerte. ¿Cómo puedo obtener la posición real de un nodo en la ventana después de traducir y escalar el diseño?

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

Cualquier orientación sería apreciada.

EDITAR: el ''nodo'' anterior es un nodo de diseño de fuerza, por lo que la simulación establece las propiedades x e y permanecen constantes después de que la simulación se detenga, independientemente del tipo de transformación que se aplique.

EDITAR: La estrategia que estoy usando para transformar el SVG proviene del comportamiento del zoom de d3, que se describe aquí: SVG Geometric Zoom .

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 bastante sencillo. El comportamiento del zoom de d3 entrega eventos de panorámica y zoom a un controlador, que aplica las transformaciones al elemento contenedor a través del atributo de transformación.

EDITAR: Estoy resolviendo el problema utilizando coordenadas del mouse en lugar de las coordenadas del nodo, ya que estoy interesado en la posición del nodo cuando el nodo se desplaza sobre el puntero del mouse. No es exactamente el comportamiento que busco, pero funciona en su mayor parte y es mejor que nada.

EDITAR: La solución fue obtener la matriz de transformación actual del elemento svg con element.getCTM () y luego usarla para desplazar las coordenadas x e y a un estado relativo a la pantalla. Vea abajo.


Puede probar node.getBBox() para obtener las posiciones de píxel de un cuadro delimitador ajustado alrededor de las formas del nodo después de que se haya aplicado cualquier transform . Vea aquí para más información: link .

EDITAR:

getBBox no funciona como pensaba. Dado que el rectángulo se define en términos del espacio de coordenadas transformado, siempre es relativo al padre <g> y, por lo tanto, siempre será el mismo para las formas contenidas.

Existe otra función llamada element.getBoundingClientRect que parece ser bastante compatible y devuelve su rectángulo en posición de píxel en relación con la parte superior izquierda del puerto de vista del navegador. Eso podría acercarle a lo que quiere sin tener que meterse con la matriz de transformación directamente.