我删除了我之前的答案,因为它是完全错误的,希望这是一个更好的答案:
<div>
<svg id="svg" width="600" height="600" version="1.1" viewBox="0 0 600 600" xmlns="http://www.w3.org/2000/svg">
<g id="svgElem" transform="rotate(-50, 240, 174)" fill="#A1B6FF">
<path transform="translate(100, 100)"
d="M0, 0 Q 140 128.76 280 0 v 148 Q 140 276.76 0 148 v -148z">
</path>
</g>
</svg>
</div>
<script type="text/javascript">
let svgElem = document.getElementById('svgElem');
let bBox = svgElem.getBBox();
console.dir(bBox);
</script>
getBBox 返回的 SVGRect 与 Firefox/Chromium 相同。
但是正如here on MDN
所述
返回值是一个 SVGRect 对象,它定义了边界框。该值与应用于它或父元素的任何转换属性无关
因此,在以这种方式应用转换之前,您总是会得到 svg 的边界框。如果您使用 getBoundingClientRect 来获取 DOMRect,您会发现 Chrome 似乎只是将转换应用于原始边界矩形,然后计算其边界框。
你可以用这样的东西来达到同样的效果(或多或少无用的代码只是为了说明):
<script type="text/javascript">
const svg = document.getElementById('svg');
let svgElem = document.getElementById('svgElem');
const bBox = svgElem.getBBox(); // MDN: The returned value is a SVGRect object, which defines the bounding box. This value is irrespective of any transformation attribute applied to it or the parent elements
console.dir(bBox);
const boundingClientRect = svgElem.getBoundingClientRect();
console.dir(boundingClientRect);
// create a rect without transforms
const rect1 = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
rect1.setAttribute('x', bBox.x);
rect1.setAttribute('y', bBox.y);
rect1.setAttribute('width', bBox.width);
rect1.setAttribute('height', bBox.height);
rect1.setAttribute('fill', '#00ff0040');
svg.appendChild(rect1);
const ctm = svgElem.getCTM();
const topLeftX = ctm.a * bBox.x + ctm.c * bBox.y + ctm.e;
const topLeftY = ctm.b * bBox.x + ctm.d * bBox.y + ctm.f;
const topRightX = ctm.a * (bBox.x + bBox.width) + ctm.c * bBox.y + ctm.e;
const topRightY = ctm.b * (bBox.x + bBox.width) + ctm.d * bBox.y + ctm.f;
const bottomLeftX = ctm.a * bBox.x + ctm.c * (bBox.y + bBox.height) + ctm.e;
const bottomLeftY = ctm.b * bBox.x + ctm.d * (bBox.y + bBox.height) + ctm.f;
const bottomRightX = ctm.a * (bBox.x + bBox.width) + ctm.c * (bBox.y + bBox.height) + ctm.e;
const bottomRightY = ctm.b * (bBox.x + bBox.width) + ctm.d * (bBox.y + bBox.height) + ctm.f;
const x = Math.min(topLeftX, topRightX, bottomLeftX, bottomRightX);
const y = Math.min(topLeftY, topRightY, bottomLeftY, bottomRightY);
const width = Math.max(topLeftX, topRightX, bottomLeftX, bottomRightX) - x;
const height = Math.max(topLeftY, topRightY, bottomLeftY, bottomRightY) - y;
const rect2 = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
rect2.setAttribute('x', x);
rect2.setAttribute('y', y);
rect2.setAttribute('width', width);
rect2.setAttribute('height', height);
rect2.setAttribute('fill', '#ff000040');
svg.appendChild(rect2);
</script>
或者您可以查看 Firefox/Chromium 的开发者工具来查看差异(只是说建立一个小组也不起作用)。
也许 SVG 第 2 版将来会有所作为:
Chrome Platfor Status SVG2
那么现在呢?如果 getBBox 是唯一似乎有效但仅适用于没有内部转换的 svg 的函数,是否可以使用 javascript 动态应用这些转换?
原来有人加倍努力:
flatten.js
将脚本放在文件“flatten.js”中,如果仍然存在,则删除顶部的剩余部分(html、标题..)
<div>
<svg id="svg" width="600" height="600" version="1.1" viewBox="0 0 600 600" xmlns="http://www.w3.org/2000/svg">
<g id="svgElem" transform="rotate(-50, 240, 174)" fill="#A1B6FF">
<path transform="translate(100, 100)"
d="M0, 0 Q 140 128.76 280 0 v 148 Q 140 276.76 0 148 v -148z">
</path>
</g>
</svg>
</div>
<script src="flatten.js"></script>
<script type="text/javascript">
const svg = document.getElementById('svg');
let svgElemClone = document.getElementById('svgElem').cloneNode(true); // flatten will directly change the element so a clone is made
svgElemClone.id = 'svgElemClone';
svg.appendChild(svgElemClone);
flatten(svgElemClone, true);
const bBox = svgElemClone.getBBox();
console.dir(bBox);
</script>
因此,这可能是获得“真实”边界框的一种解决方法。
关于getBoundingClientRect:
MDN 说:“返回值是一个 DOMRect 对象,它是包含整个元素的最小矩形,包括它的内边距和边框宽度。”
恕我直言,Chromium 的实现中有一个错误。