javascript 获取元素的边界框考虑其变换
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10623809/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Get bounding box of element accounting for its transform
提问by Phrogz
Given this SVG:
鉴于此 SVG:
<svg xmlns="http://www.w3.org/2000/svg">
<rect width="50" height="50"
transform="translate(75,75) rotate(45) translate(-25,-25)" />
<script>
var bb = document.querySelector('rect').getBBox();
console.log([bb.x,bb,y,bb.width,bb.height]);
</script>
</svg>?
The resulting output is [0, 0, 50, 50]
.
The desired result is [39.645,39.645,70.711,70.711]
.
结果输出为[0, 0, 50, 50]
。
想要的结果是[39.645,39.645,70.711,70.711]
。
Visual Version: http://jsfiddle.net/2wpju/7/
可视化版本:http: //jsfiddle.net/2wpju/7/
What's the simplest, most efficient way to calculate the bounding box of an element with respect to its parent element?
计算元素相对于其父元素的边界框的最简单、最有效的方法是什么?
Below is the best answer I've come up with so far, but there are two problems:
以下是迄今为止我想出的最佳答案,但有两个问题:
- It seems like a lot of work for what might be handled better, and
- As seen in this demoit's not necessarily the minimum bounding box (notice the upper circle), since the axis-aligned bounding box of a rotated axis-aligned bounding box always increases the size.
- 似乎有很多工作可以更好地处理,并且
- 正如在这个演示中看到的,它不一定是最小的边界框(注意上面的圆圈),因为旋转的轴对齐边界框的轴对齐边界框总是增加尺寸。
// Calculate the bounding box of an element with respect to its parent element
function transformedBoundingBox(el){
var bb = el.getBBox(),
svg = el.ownerSVGElement,
m = el.getTransformToElement(el.parentNode);
// Create an array of all four points for the original bounding box
var pts = [
svg.createSVGPoint(), svg.createSVGPoint(),
svg.createSVGPoint(), svg.createSVGPoint()
];
pts[0].x=bb.x; pts[0].y=bb.y;
pts[1].x=bb.x+bb.width; pts[1].y=bb.y;
pts[2].x=bb.x+bb.width; pts[2].y=bb.y+bb.height;
pts[3].x=bb.x; pts[3].y=bb.y+bb.height;
// Transform each into the space of the parent,
// and calculate the min/max points from that.
var xMin=Infinity,xMax=-Infinity,yMin=Infinity,yMax=-Infinity;
pts.forEach(function(pt){
pt = pt.matrixTransform(m);
xMin = Math.min(xMin,pt.x);
xMax = Math.max(xMax,pt.x);
yMin = Math.min(yMin,pt.y);
yMax = Math.max(yMax,pt.y);
});
// Update the bounding box with the new values
bb.x = xMin; bb.width = xMax-xMin;
bb.y = yMin; bb.height = yMax-yMin;
return bb;
}
回答by Progo
回答by Minok
That seems to be the current method, unless/until getBBox() considers the transform on the object as directly defining the bounding box, even though SVG 1.1 SE defines:
这似乎是当前的方法,除非/直到 getBBox() 将对象上的变换视为直接定义边界框,即使SVG 1.1 SE 定义了:
SVGRect getBBox() 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). Note that getBBox must return the actual bounding box at the time the method was called, even in case the element has not yet been rendered.
SVGRect getBBox() 返回当前用户空间中所有包含的图形元素的几何图形上的紧密边界框(即,在应用 'transform' 属性之后,如果有的话),不包括描边、裁剪、遮罩和过滤效果)。请注意,即使在元素尚未呈现的情况下, getBBox 也必须返回调用该方法时的实际边界框。
I guess its all about how you define 'current user space'
我想这完全取决于您如何定义“当前用户空间”
回答by GeeWhizBang
This is an answer that accounts for rotated ellipses. The transformation method works OK for rectangles but on ellipses returns a bounding box considerably larger than the object if it is rotated.
这是一个考虑旋转椭圆的答案。转换方法适用于矩形,但在椭圆上,如果旋转,则返回一个比对象大得多的边界框。
The simpler answer of using the builtin JavaScript function isn't very useful to me because it would still have to be translated back into the drawing space.
使用内置 JavaScript 函数的简单答案对我来说不是很有用,因为它仍然必须被转换回绘图空间。
This code doesn't handle skew or scale for ellipses (but does for any other object), but in my program at http://nwlink.com/~geoffrey/svgBuildI intend to handle skew and scale by drawing an entirely new ellipse with the skew or scale applied.
此代码不处理椭圆的倾斜或缩放(但处理任何其他对象),但在我位于http://nwlink.com/~geoffrey/svgBuild 的程序中,我打算通过绘制一个全新的椭圆来处理倾斜和缩放应用了倾斜或比例。
The formula for the ellipses came from here: How do you calculate the axis-aligned bounding box of an ellipse?much thanks for the help there.
椭圆的公式来自这里:如何计算椭圆的轴对齐边界框?非常感谢那里的帮助。
el is a d3.js element, not the node.
el 是一个 d3.js 元素,而不是节点。
function toNumberIfNumeric(x) {
var result = parseFloat(x);
return isNaN(result) ? x : result;
}
function getAttrs(el, attributes) {
var result = {};
attributes.forEach(function (attr) {
result[attr] = toNumberIfNumeric(el.attr(attr));
});
return result;
}
function getTransformedBbox(el, referenceNode) {
var node = el.node();
switch (node.nodeName) {
case "ellipse":
var rotate = getTransform(el, "rotate");
if (rotate.angle === 0) {
return node.getBBox();
}
var attr = getAttrs(el, ["cx", "cy", "rx", "ry"]);
var radians = rotate.angle / 180 * Math.PI;
var radians90 = radians + Math.PI / 2;
var ux = attr.rx * Math.cos(radians);
var uy = attr.rx * Math.sin(radians);
var vx = attr.ry * Math.cos(radians90);
var vy = attr.ry * Math.sin(radians90);
var halfWidth = Math.sqrt(ux * ux + vx * vx);
var halfHeight = Math.sqrt(uy * uy + vy * vy);
return {
x: attr.cx - halfWidth,
y: attr.cy - halfHeight,
width: 2 * halfWidth,
height: 2 * halfHeight
};
default:
var bb = node.getBBox(),
svg = node.ownerSVGElement,
m = node.getTransformToElement(referenceNode);
// Create an array of all four points for the original bounding box
var pts = [
svg.createSVGPoint(), svg.createSVGPoint(),
svg.createSVGPoint(), svg.createSVGPoint()
];
pts[0].x = bb.x;
pts[0].y = bb.y;
pts[1].x = bb.x + bb.width;
pts[1].y = bb.y;
pts[2].x = bb.x + bb.width;
pts[2].y = bb.y + bb.height;
pts[3].x = bb.x;
pts[3].y = bb.y + bb.height;
// Transform each into the space of the parent,
// and calculate the min/max points from that.
var xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity;
pts.forEach(function (pt) {
pt = pt.matrixTransform(m);
xMin = Math.min(xMin, pt.x);
xMax = Math.max(xMax, pt.x);
yMin = Math.min(yMin, pt.y);
yMax = Math.max(yMax, pt.y);
});
// Update the bounding box with the new values
bb.x = xMin;
bb.width = xMax - xMin;
bb.y = yMin;
bb.height = yMax - yMin;
return bb;
}
}
回答by Timo K?hk?nen
One way is to make a raphael set and get bb of it. Or wrap all elements inside g and get bb of g, i assume that g should be fastest.
一种方法是制作一个拉斐尔集并获得它。或者将所有元素包裹在 g 中并获得 g 的 bb,我认为 g 应该是最快的。