如何计算getBBox()SVGRect?

时间:2011-05-30 17:31:36

标签: svg

我有一个包含一个或多个g元素的path元素。正如我在another question中提到的,我通过计算g属性来缩放和翻译transform元素,以便它适合画布另一部分的网格。

使用两个矩形之间的差异进行计算,getBBox()元素中的g和网格周围的矩形。

以下是问题 - 在我进行转换后,我会更新g元素的内容并再次调用getBBox()不用删除{{1} }。生成的矩形似乎是在不考虑transform的情况下计算的。我原以为它会反映这种变化。这种行为是否与SVG规范一致?如何获取变换矩形的边界框?

这是BTW,在Firefox 4中运行的HTML 5文档中,如果这有任何区别的话。

更新:显然这种行为显然违反了规范。来自文字here at w3c

  

SVGRect getBBox()

     

返回当前用户空间中的紧束缚框(即,在应用'transform'属性后,如果有的话)在所有包含的图形元素的几何上,不包括描边,剪裁,遮罩和滤镜效果)。请注意,getBBox必须在调用方法时返回实际的边界框,即使元素尚未呈现也是如此。

我是否正确阅读?如果是这样,这似乎是Firefox使用的SVG实现中的勘误表;我没有机会尝试任何其他的。如果有人能指出我在哪里,我会提交错误报告。

7 个答案:

答案 0 :(得分:18)

人们常常对getBBox和getBoundingClientRect的行为差异感到困惑。

getBBox 是SVG Element的本机方法,相当于查找HTML DOM元素的偏移量/客户端宽度。宽度和高度即使在旋转元素时也不会改变。它不能用于HTML DOM元素。

getBoundingClientRect 对HTML和SVG元素都很常见。当元素旋转或更多元素被分组时,bounded rectangle宽度和高度会发生变化。

答案 1 :(得分:15)

您看到的行为是正确的,并且符合规范。 应用变换,然后以“当前用户单位”(即当前用户空间)计算bbox。因此,如果要查看元素转换的结果,则需要查看父节点或类似节点的bbox。 这有点令人困惑,但在SVG Tiny 1.2 spec for SVGLocatable中解释得更好 这包含了许多澄清它应该做什么的例子。

答案 2 :(得分:6)

至少有两种简单但有点哈哈的方法可以做你所要求的...如果有更好的(不那么黑客)的方式,我还没有找到它们

EASY HACKY#1:
a)设置一个与group.getBBox()返回的“未转换”bbox匹配的矩形 b)将该组的“未应用的转换”应用于该矩形 c)rect.getBBox()现在应该返回你正在寻找的bbox

EASY HACKY#2 :(仅在镀铬中测试)
a)使用element.getBoundingClientRect(),它返回足够的信息来构建你正在寻找的bbox

答案 3 :(得分:4)

显然getBBox()不会考虑转换。

我可以在这里指出你,遗憾的是我无法让它发挥作用:http://tech.groups.yahoo.com/group/svg-developers/message/22891

答案 4 :(得分:3)

SVG小组有令人讨厌的做法 - 不要累积所有转换。我有办法解决这个问题。我正在使用自己的属性来存储我在任何进一步转换中包含的当前转换数据。使用XML兼容的属性,如alttext,value,name ....或只是x和y,用于存储累积值作为属性。

示例:

<g id="group" x="20" y="100" transform="translate(20, 100)">
<g id="subgroup" alttext="45" transform="rotate(45)">
<line...etc...

因此,当我进行转换时,我正在使用那些手工制作的属性值,当写回来时,我正在编写变换和相同的值,并使用我为保持所有累积所做的属性值。

轮换示例:

function symbRot(evt) {

  evt.target.ondblclick = function () {

    stopBlur();
    var ptx=symbG.parentNode.lastChild.getAttribute("cx");
    var pty=symbG.parentNode.lastChild.getAttribute("cy");
    var currRot=symbG.getAttributeNS(null, "alttext");

    var rotAng;
    if (currRot == 0) {
      rotAng = 90
    } else if (currRot == 90) {
      rotAng = 180
    } else if (currRot == 180) {
      rotAng = 270
    } else if (currRot == 270) {
      rotAng = 0
    };
    symbG.setAttributeNS(null, "transform", "rotate(" + rotAng + "," + ptx + ", " + pty + ")");
    symbG.setAttributeNS(null, "alttext", rotAng );
  };
}

答案 5 :(得分:3)

我创建了一个辅助函数,它返回svg元素的各种度量(也是转换元素的bbox)。

代码在这里: https://stackoverflow.com/a/13111598/1691517

完整的功能示例如下: http://jsbin.com/acowaq/1

答案 6 :(得分:1)

以下代码考虑了父母,本身以及孩子的转换(矩阵或其他方式)。因此,它将在<g>元素上工作。

您通常希望将父<svg>作为第三个参数toElement传递,以便在<svg>的坐标空间中返回计算出的边界框(通常是协调我们关心的空间)。

/**
 * @param {SVGElement} element - Element to get the bounding box for
 * @param {boolean} [withoutTransforms=false] - If true, transforms will not be calculated
 * @param {SVGElement} [toElement] - Element to calculate bounding box relative to
 * @returns {SVGRect} Coordinates and dimensions of the real bounding box
 */
function getBBox(element, withoutTransforms, toElement) {

  var svg = element.ownerSVGElement;

  if (!svg) {
    return { x: 0, y: 0, cx: 0, cy: 0, width: 0, height: 0 };
  }

  var r = element.getBBox(); 

  if (withoutTransforms) {
    return {
      x: r.x,
      y: r.y,
      width: r.width,
      height: r.height,        
      cx: r.x + r.width / 2,
      cy: r.y + r.height / 2
    };
  }

  var p = svg.createSVGPoint(); 

  var matrix = (toElement || svg).getScreenCTM().inverse().multiply(element.getScreenCTM()); 

  p.x = r.x;
  p.y = r.y;
  var a = p.matrixTransform(matrix);

  p.x = r.x + r.width;
  p.y = r.y;
  var b = p.matrixTransform(matrix);

  p.x = r.x + r.width;
  p.y = r.y + r.height;
  var c = p.matrixTransform(matrix);

  p.x = r.x;
  p.y = r.y + r.height;
  var d = p.matrixTransform(matrix);

  var minX = Math.min(a.x, b.x, c.x, d.x);
  var maxX = Math.max(a.x, b.x, c.x, d.x);
  var minY = Math.min(a.y, b.y, c.y, d.y);
  var maxY = Math.max(a.y, b.y, c.y, d.y);

  var width = maxX - minX;
  var height = maxY - minY;

  return {
    x: minX,
    y: minY,
    width: width,
    height: height,        
    cx: minX + width / 2,
    cy: minY + height / 2
  };
}