Getting an SVG Element's Full Bounding Box
Getting the fully-transformed bounding box of a given SVG element is something quite handy and not given for free by the SVG DOM right now. However, the SVG DOM provides all the required foundations to compute such a bounding box, by providing the SVGLocatable::getBBox(), SVGLocatable::getScreenCTM() and SVGPoint::matrixTransform() methods mostly. So, with no further ado, here's the code to a utility getScreenBBox(element) function:
function getScreenBBox (element) {
// macro to create an SVGPoint object
function createPoint (x, y) {
var point = document.documentElement.createSVGPoint();
point.x = x;
point.y = y;
return point;
}
// macro to create an SVGRect object
function createRect (x, y, width, height) {
var rect = document.documentElement.createSVGRect();
rect.x = x;
rect.y = y;
rect.width = width;
rect.height = height;
return rect;
}
// get the complete transformation matrix
var matrix = element.getScreenCTM();
// get the bounding box of the target element
var box = element.getBBox();
// create an array of SVGPoints for each corner
// of the bounding box and update their location
// with the transform matrix
var corners = [];
var point = createPoint(box.x, box.y);
corners.push( point.matrixTransform(matrix) );
point.x = box.x + box.width;
point.y = box.y;
corners.push( point.matrixTransform(matrix) );
point.x = box.x + box.width;
point.y = box.y + box.height;
corners.push( point.matrixTransform(matrix) );
point.x = box.x;
point.y = box.y + box.height;
corners.push( point.matrixTransform(matrix) );
var max = createPoint(corners[0].x, corners[0].y);
var min = createPoint(corners[0].x, corners[0].y);
// identify the new corner coordinates of the
// fully transformed bounding box
for (var i = 1; i < corners.length; i++) {
var x = corners[i].x;
var y = corners[i].y;
if (x < min.x) {
min.x = x;
}
else if (x > max.x) {
max.x = x;
}
if (y < min.y) {
min.y = y;
}
else if (y > max.y) {
max.y = y;
}
}
// return the bounding box as an SVGRect object
return createRect(min.x, min.y, max.x - min.x, max.y - min.y);
}
// macro to create an SVGPoint object
function createPoint (x, y) {
var point = document.documentElement.createSVGPoint();
point.x = x;
point.y = y;
return point;
}
// macro to create an SVGRect object
function createRect (x, y, width, height) {
var rect = document.documentElement.createSVGRect();
rect.x = x;
rect.y = y;
rect.width = width;
rect.height = height;
return rect;
}
// get the complete transformation matrix
var matrix = element.getScreenCTM();
// get the bounding box of the target element
var box = element.getBBox();
// create an array of SVGPoints for each corner
// of the bounding box and update their location
// with the transform matrix
var corners = [];
var point = createPoint(box.x, box.y);
corners.push( point.matrixTransform(matrix) );
point.x = box.x + box.width;
point.y = box.y;
corners.push( point.matrixTransform(matrix) );
point.x = box.x + box.width;
point.y = box.y + box.height;
corners.push( point.matrixTransform(matrix) );
point.x = box.x;
point.y = box.y + box.height;
corners.push( point.matrixTransform(matrix) );
var max = createPoint(corners[0].x, corners[0].y);
var min = createPoint(corners[0].x, corners[0].y);
// identify the new corner coordinates of the
// fully transformed bounding box
for (var i = 1; i < corners.length; i++) {
var x = corners[i].x;
var y = corners[i].y;
if (x < min.x) {
min.x = x;
}
else if (x > max.x) {
max.x = x;
}
if (y < min.y) {
min.y = y;
}
else if (y > max.y) {
max.y = y;
}
}
// return the bounding box as an SVGRect object
return createRect(min.x, min.y, max.x - min.x, max.y - min.y);
}
