Conseguir cuadro delimitador de elemento de contabilidad para su transformación

Dado este SVG:

<svg xmlns="http://www.w3.org/2000/svg">
  <rect width="50" height="50"
        transform="translate(75,75) rotate(45) translate(-25,-25)" />
  <script>
    var bb = document.querySelector('rect').getBBox();
    console.log([bb.x,bb,y,bb.width,bb.height]);
  </script>
</svg>

La salida resultante es [0, 0, 50, 50].

El resultado deseado es [39.645,39.645,70.711,70.711].

Visual De La Versión: http://jsfiddle.net/2wpju/7/

¿Cuál es la manera más simple y eficiente para calcular el cuadro delimitador de un elemento con respecto a su elemento padre?


A continuación es la mejor respuesta que he encontrado hasta ahora, pero hay dos problemas:

  1. Parece como un montón de trabajo por lo que se podría manejar mejor, y
  2. Como se ve en esta demo no es necesariamente el mínimo cuadro delimitador (aviso al círculo superior), ya que el eje alineado a la caja de contorno de una rotación del eje alineado cuadro delimitador siempre aumenta el tamaño.

 

//Calculate the bounding box of an element with respect to its parent element
function transformedBoundingBox(el){
  var bb  = el.getBBox(),
      svg = el.ownerSVGElement,
      m   = el.getTransformToElement(el.parentNode);

  //Create an array of all four points for the original bounding box
  var pts = [
    svg.createSVGPoint(), svg.createSVGPoint(),
    svg.createSVGPoint(), svg.createSVGPoint()
  ];
  pts[0].x=bb.x;          pts[0].y=bb.y;
  pts[1].x=bb.x+bb.width; pts[1].y=bb.y;
  pts[2].x=bb.x+bb.width; pts[2].y=bb.y+bb.height;
  pts[3].x=bb.x;          pts[3].y=bb.y+bb.height;

  //Transform each into the space of the parent,
  //and calculate the min/max points from that.    
  var xMin=Infinity,xMax=-Infinity,yMin=Infinity,yMax=-Infinity;
  pts.forEach(function(pt){
    pt = pt.matrixTransform(m);
    xMin = Math.min(xMin,pt.x);
    xMax = Math.max(xMax,pt.x);
    yMin = Math.min(yMin,pt.y);
    yMax = Math.max(yMax,pt.y);
  });

  //Update the bounding box with the new values
  bb.x = xMin; bb.width  = xMax-xMin;
  bb.y = yMin; bb.height = yMax-yMin;
  return bb;
}
  • Curiosamente, el «demasiado grande» ejemplo de la girado círculo es el mismo como el resultado de una llamada a getBoundingClientRect() para el elemento, en todos los principales navegadores. Parece que internamente hacer la misma cosa (encontrar el local rect delimitador y, a continuación, transformar esos puntos en el espacio de la pantalla).
  • Comentario anterior no se aplica a Firefox.
  • genial, funciona como se espera
  • Esto me ha ayudado mucho, ya que he estado luchando con las transformaciones de matriz con svg y delimitación. Yo estaba buscando una solución más simple, así que puedo conseguir mi cabeza alrededor de la más fácil, sin embargo, este miró a la obra de inmediato.
  • Creo que quiso decir el resultado deseado ser: [39.64466,39.64466,110.35533,110.35533]
  • Este es un pequeño gran función de utilidad, pero por desgracia Google Chrome quitado el getTransformToElement() método recientemente. La solución es el polyfil SVGElement : if (typeof SVGElement.el prototipo.getTransformToElement === ‘undefined’) { SVGElement.el prototipo.getTransformToElement = function(toElement) { return toElement.getScreenCTM().inversa().multiplicar(este.getScreenCTM()); }; }

InformationsquelleAutor Phrogz | 2012-05-16

4 Kommentare

  1. 3

    Todo lo que necesitas es usar esta simple función de Javascript:

    object.getBoundingClientRect();

    Es compatible con:

    Chrome  Firefox (Gecko)   Internet Explorer Opera   Safari
    1.0           3.0 (1.9)   4.0               (Yes)   4.0

    Clic aquí para ver más acerca de él. Enlace

    • Que se aplique a todos los transforma en cada elemento padre, no sólo el que está en el mismo elemento.
    • Esto funciona, pero ten en cuenta que se calcula en el espacio de la pantalla, no en el espacio de página. Por lo tanto, para SVGs más grande que la ventana, getBoundingClientRect dará diferentes resultados en función de desplazamiento desplazamiento.
  2. 2

    Una forma es hacer una raphael set y get de bb de la misma. O envolver todos los elementos dentro de g y obtener bb de g, supongo que g debe ser más rápido.

  3. 2

    Que parece ser el método actual, a menos que/hasta getBBox() considera que la transformación en el objeto directamente en la definición del cuadro delimitador, aunque SVG 1.1 SE define:

    SVGRect getBBox()
    Devuelve el apretado cuadro delimitador en el actual espacio de usuario (es decir, después de la aplicación de la ‘transformación’ atributo, si las hubiera) en la geometría de todos los contenidos de elementos gráficos, exclusivo de caricias, de recorte, máscaras y efectos de filtro). Tenga en cuenta que getBBox debe devolver el actual cuadro delimitador en el momento en que el método fue llamado, incluso en caso de que el elemento no ha sido prestado.

    Supongo que es todo acerca de cómo definir » actual espacio de usuario

  4. 2

    Esta es una respuesta que cuentas para girar elipses. El método de transformación funciona bien para los rectángulos sino en elipses devuelve un cuadro delimitador considerablemente más grande que el objeto si es rotada.

    La respuesta más simple de usar la integrada de la función de JavaScript no es muy útil para mí porque todavía tendría que ser traducido de nuevo en el espacio de dibujo.

    Este código no manejar sesgar o escala de puntos suspensivos (pero no para cualquier otro objeto), pero en mi programa a http://nwlink.com/~geoffrey/svgBuild tengo la intención de manejar el sesgo y la escala de dibujo de una forma totalmente nueva elipse con el skew o escala que se aplica.

    La fórmula de la elipse de vino de aquí: ¿Cómo se calcula el eje alineado a la caja de contorno de una elipse? mucho gracias por la ayuda allí.

    el es un d3.js elemento, no el nodo.

    function toNumberIfNumeric(x) {
        var result = parseFloat(x);
        return isNaN(result) ? x : result;
    }
    
    function getAttrs(el, attributes) {
        var result = {};
        attributes.forEach(function (attr) {
            result[attr] = toNumberIfNumeric(el.attr(attr));
        });
        return result;
    }    
    
    function getTransformedBbox(el, referenceNode) {
    
        var node = el.node();
    
        switch (node.nodeName) {
            case "ellipse":
    
                var rotate = getTransform(el, "rotate");
                if (rotate.angle === 0) {
                    return node.getBBox();
                }
                var attr = getAttrs(el, ["cx", "cy", "rx", "ry"]);
                var radians = rotate.angle / 180 * Math.PI;
                var radians90 = radians + Math.PI / 2;
    
                var ux = attr.rx * Math.cos(radians);
                var uy = attr.rx * Math.sin(radians);
                var vx = attr.ry * Math.cos(radians90);
                var vy = attr.ry * Math.sin(radians90);
    
                var halfWidth = Math.sqrt(ux * ux + vx * vx);
                var halfHeight = Math.sqrt(uy * uy + vy * vy);
    
                return {
                    x: attr.cx - halfWidth,
                    y: attr.cy - halfHeight,
                    width: 2 * halfWidth,
                    height: 2 * halfHeight
                };
    
            default:
                var bb = node.getBBox(),
                    svg = node.ownerSVGElement,
                    m = node.getTransformToElement(referenceNode);
    
                //Create an array of all four points for the original bounding box
                var pts = [
                    svg.createSVGPoint(), svg.createSVGPoint(),
                    svg.createSVGPoint(), svg.createSVGPoint()
                ];
                pts[0].x = bb.x;
                pts[0].y = bb.y;
                pts[1].x = bb.x + bb.width;
                pts[1].y = bb.y;
                pts[2].x = bb.x + bb.width;
                pts[2].y = bb.y + bb.height;
                pts[3].x = bb.x;
                pts[3].y = bb.y + bb.height;
    
                //Transform each into the space of the parent,
                //and calculate the min/max points from that.    
                var xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity;
                pts.forEach(function (pt) {
                    pt = pt.matrixTransform(m);
                    xMin = Math.min(xMin, pt.x);
                    xMax = Math.max(xMax, pt.x);
                    yMin = Math.min(yMin, pt.y);
                    yMax = Math.max(yMax, pt.y);
                });
    
                //Update the bounding box with the new values
                bb.x = xMin;
                bb.width = xMax - xMin;
                bb.y = yMin;
                bb.height = yMax - yMin;
                return bb;
        }
    }

Kommentieren Sie den Artikel

Bitte geben Sie Ihren Kommentar ein!
Bitte geben Sie hier Ihren Namen ein

Pruebas en línea