Calculating the Bounding Box of SVG Group Elements

Today I encountered a puzzling scenario involving bounding box calculations, and it seems that I have yet to fully understand the situation.

To clarify, a bounding box is described as the smallest box in which an untransformed element can fit within.

I had always believed that for groups of elements, this meant obtaining the combined bounding box of all children.

However, what I found today was unexpected:

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

I expected all boxes to be identical, but clearly that is not the case. The outer box does not simply encompass the inner elements (which would have been the #inner's bbox). Instead, it takes into consideration the transformation applied to the inner elements.

Therefore, can it be concluded that the bbox of a group is essentially the union of the TRANSFORMED bboxes of its children? Or more precisely, the combination of all getBoundingClientRect calls (assuming scroll is disregarded since getCoundingClientRect ignores scroll)?

I would greatly appreciate a reference directing me to the specific section of the specifications regarding this matter.

Answer №1

The getBBox function returns the bounding box based on the element's transformed coordinate system.

It calculates the tight bounding box in the current user space, taking into account any transformations applied to the elements within it.

Keep in mind that the outer SVG element uses a different coordinate system than the inner elements due to their transforms.

On the other hand, getBoundingClientRect works with the global coordinate system.

Answer №2

Experience the visual representation of the #outer BBox in action as it rotates in this demonstration.

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>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What are the potential drawbacks of combining the useState hook with Context API in React.js?

Within my code, I establish a context and a provider in the following manner. Utilizing useState() within the provider enables me to manage state while also implementing functions passed as an object to easily destructure elements needed in child component ...

Issues with Angular JS page loading when utilizing session and local storage in Google Chrome

I'm currently learning about AngularJS and I stumbled upon this helpful tutorial http://embed.plnkr.co/dd8Nk9PDFotCQu4yrnDg/ for building a simple SPA page. Everything was working smoothly in Firefox and IE, except when it came to using Local and Sess ...

Dealing with the issue of asynchronous operations in a controller using async/await function

Something strange is happening here, even though I'm using async await: const Employee = require('../models/employee'); const employeeCtrl = {}; employeeCtrl.getEmployees = async (req, res) => { const employees = await Employee.find( ...

Reduce and combine JavaScript files without the need for Grunt or Gulp

My project involves working with over 50 JavaScript files that I want to combine and compress to optimize file size and minimize HTTP requests. The catch is, the client's system does not have Node or npm installed. How can I accomplish this task witho ...

Is it possible for a function to alter the 'this' object in JavaScript?

Referencing the MDN article on this keyword in JavaScript When not in strict mode, 'this' in a function will point to the global object. However, attempting to modify a global variable within a function does not result as expected. Is there an ...

Prevent a specific folder from being included in expressjs routing

When using my expressjs app, I load public assets like this: app.use(express.static(__dirname + '/public')); Afterwards, I redirect all requests to the index, where every path is handled by Backbone app.get('*', routes.index); I am ...

Uniting 2 streams to create a single observable

I am in the process of merging 2 different Observables. The first Observable contains a ShoppingCart class, while the second one holds a list of ShoppingItems. My goal is to map the Observable with shopping cart items (Observable<ShoppingItems) to the i ...

Issue with clientHeight not functioning properly with line breaks in Angular 2 application after ngAfterViewInit

I have successfully created a Gridify page in my Angular 2 application using the Gridify library. To initialize it, I've utilized a custom ngAfterViewChecked method: ngAfterViewChecked() { var selector = document.querySelector('.read-grid& ...

Issue TS7053 occurs when trying to access any index of the target of a React.FormEvent<HTMLFormElement>

I've been working on adapting this tutorial to React and TypeScript. Here is the code snippet I have implemented for handling the onSubmit event: const handleSignUp = (event: React.FormEvent<HTMLFormElement>) => { event.preventDefault(); ...

What is the best way to convert a circular JSON object to a string

Is there a way to stringify a complex JSON object without encountering the "Converting circular structure to JSON" error? I also need its parser. I am facing issues every time I try to use JSON.stringify and encounter the "Converting circular structure to ...

The response from the $http POST request is not returning the expected

I am facing an issue where the $http POST method is not returning the expected response. The required data is located within config instead of data This is my Http POST request: for (var i = 0; i < filmService.filmData.length; i++) { filmData.pu ...

I'm encountering a strange issue where Node JS is mistakenly claiming that the method doesn't exist, even though

Ah, it seems I've encountered an error in my test project. The issue lies with Node JS not being able to locate the getStr function within the Another object. Allow me to share the code with you: test.js var Another = require('./another.js&apo ...

The re-rendering of a functional component utilizing React.useCallback and React.memo() is occurring

I was delving into React concepts and eager to experiment with some new things. Here are a few questions that crossed my mind: Does every functional child component re-render when the parent state changes, even if it doesn't have any props? It seems ...

What is the process for enabling HLS.js to retrieve data from the server side?

I have successfully implemented a video player using hls.js, and I have some ts files stored in https:/// along with an m3u8 file. To read the content of the m3u8 file, I used PHP to fetch it and sent the data to JavaScript (res["manifest"] = the content ...

Encountering a [$injector:modulerr] error while attempting to include modules in ZURB Foundation for Apps

I am currently working on a project that involves specific authentication which is functioning well in Ionic. My task now is to incorporate the same authentication system into the admin panel exclusively for web devices. I have already completed the instal ...

jQuery Refuses to Perform Animation

I'm facing an issue with animating a specific element using jQuery while scrolling down the page. My goal is to change the background color of the element from transparent to black, but so far, my attempts have been unsuccessful. Can someone please pr ...

Enable only the current week days on the multiple date picker feature

Can anyone recommend a date picker that only shows the current week and allows for multiple date selections by the user? I found this jsfiddle which limits the display to the current week, but it doesn't support selecting multiple dates. I attempted ...

Issue with sending multiple files using FormData and axios in Vuex with Laravel. Server side consistently receiving null. Need help troubleshooting the problem

After trying the solution from my previous question Vuex + Laravel. Why axios sends any values but only not that one, which come's with method's parameter?, I realized that it only works when passing a single argument in axios. I managed to succe ...

The daily scripture quote from the ourmanna.com API may occasionally fail to appear

I've been trying to display the daily verse from ourmanna.com API using a combination of HTML and JS code, but I'm encountering an issue where the verse doesn't always show up. I'm not sure if this problem is on the side of their API or ...

Choose a procedure to reset to the original setting

I'm attempting to achieve something that seems straightforward, but I'm having trouble finding a solution. I have a <select> tag with 5 <option> elements. All I want to do is, when I click a button (which has a function inside of it), ...