Reputation: 8474
I just came across a weired case of bouncing box calculation and it seems I did not grasp the whole truth yet.
First of all, a bounding box is defined as the tightest box, an untransformed element can be enclosed with.
I always was under the impression, that for groups, that means, that it gets basically the union of the bounding box of all children.
However, today I came across this:
<g id="outer">
<g id="inner" transform="translate(100, 100)">
<rect x="0" y="0" width="100" height="100" />
</g>
</g>
The bounding boxes of the elements are as follows:
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
My expectation would have been, that all boxes are the same but as you can see, the outer box is NOT the union of the inner elements (in that case it would equal the #inner's bbox). Instead it takes into account the transformation of the inner elements.
So, is it right to say, that the bbox of a group is the union of the TRANSFORMED bbox's of its children? Or more programatically said, the union of all getBoundingClientRect
calls (assuming that scroll is 0 because getCoundingClientRect
ignores scroll)?
I would really appreciate a link pointing me to the correct part of the specs.
Upvotes: 1
Views: 1817
Reputation: 33044
In this demo the red polygon represents the #outer
BBox during an animation where the rect is rotating.
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>
Upvotes: 3
Reputation: 124089
The bounding box returned by getBBox is the box in the element's transformed coordinate system
Returns the tight bounding box in current user space (i.e., after application of the ‘transform’ attribute, if any) on the geometry of all contained graphics elements, exclusive of stroking, clipping, masking and filter effects)...
The outer SVG element has a different co-ordinate system. I.e. where it places the origin is not the same as the inner <g>
element because of the inner element's transform.
getBoundingClientRect operates in the global co-ordinate system however.
Upvotes: 5