svg <g>元素的BBox计算

时间:2018-12-14 07:55:50

标签: javascript svg bounding-box getboundingclientrect

我刚刚遇到了一个奇怪的情况,即弹跳盒计算,看来我还没有掌握全部真相。

首先,将边界框定义为最紧密的框,可以将未转换的元素包含在其中。

我总是给人一种印象,对于团体来说,这意味着它基本上得到了所有孩子的边界框的并集​​。

但是,今天我遇到了这个问题:

<g id="outer">
  <g id="inner" transform="translate(100, 100)">
    <rect x="0" y="0" width="100" height="100" />
  </g>
</g>

元素的边界框如下:

  • rect: x: 0, y: 0, w: 100, h: 100
  • #inner: x: 0, y: 0, w: 100, h: 100
  • #outer: x: 100, y: 100, w: 100, h: 100

我希望所有框都是相同的,但是如您所见,外框不是内部元素的并集(在这种情况下,它等于#inner的bbox)。相反,它考虑了内部元素的转换。

所以,可以说一个组的bbox是其子级 TRANSFORMED bbox的并集吗?或更用编程的方式说,所有getBoundingClientRect调用的并集(假设滚动为0,因为getCoundingClientRect忽略滚动)?

我真的很感谢链接,它指向规范的正确部分。

2 个答案:

答案 0 :(得分:5)

getBBox返回的边界框是element's transformed coordinate system

中的框
  

在所有包含的图形元素的几何图形上返回当前用户空间中的紧密边界框(即,在应用了“ transform”属性后,如果有的话),不包括笔触,剪切,蒙版和滤镜效果)

外部SVG元素具有不同的坐标系。即由于内部元素的变换,原点放置在与内部<g>元素不同的位置。

getBoundingClientRect在全局坐标系中运行。

答案 1 :(得分:3)

在此演示中,红色矩形代表矩形旋转的动画期间的#outer BBox。

const SVG_NS = 'http://www.w3.org/2000/svg';
let o = outer.getBBox()
let i = inner.getBBox()

let BBpoly = drawBBox(o); 



function drawBBox(bb){
  let p = [{x:bb.x,y:bb.y},
           {x:bb.x+bb.width,y:bb.y},
           {x:bb.x+bb.width,y:bb.y+bb.height},
           {x:bb.x,y:bb.y+bb.height}];
  let BBpoly = drawPolygon(p, BBoxes);
  return BBpoly;
}


function drawPolygon(p, parent) {
  let poly = document.createElementNS(SVG_NS, 'polygon');
  let ry = [];
  for (var i = 0; i < p.length; i++) {
    ry.push(String(p[i].x + ", " + p[i].y));
  }
  var points = ry.join(" ");
  poly.setAttributeNS(null, 'points', points);

  parent.appendChild(poly);
  return poly;
}


function updatePolygon(p,poly){
  let ry = [];
  for (var i = 0; i < p.length; i++) {
    ry.push(String(p[i].x + ", " + p[i].y));
  }
  var points = ry.join(" ");
  poly.setAttributeNS(null, 'points', points);
}

let a = 0;
function Frame(){
  requestAnimationFrame(Frame);
  inner.setAttributeNS(null,"transform", `rotate(${a}, 120,120)`)
  let bb = outer.getBBox()
  let p = [{x:bb.x,y:bb.y},
           {x:bb.x+bb.width,y:bb.y},
           {x:bb.x+bb.width,y:bb.y+bb.height},
           {x:bb.x,y:bb.y+bb.height}];
  updatePolygon(p,BBpoly);
  
  a++
}

Frame()
svg{border:1px solid; width:300px;}
polygon{fill:none; stroke:red; }
<svg viewBox="0 0 250 250">
  <g id="BBoxes"></g>
  <g id="outer">
  <g id="inner">
    <rect x="70" y="70" width="100" height="100"  />
  </g>
</g>
</svg>