通过JavaScript计算旋转元素的边界框的X,Y,高度和宽度

时间:2012-05-01 01:57:29

标签: javascript css geometry rotation bounding-box

基本上我是在问JavaScript这个问题:Calculate Bounding box coordinates from a rotated rectangle

Diagram

在这种情况下:

  • iX =旋转(蓝色)HTML元素的宽度
  • iY =旋转(蓝色)HTML元素的高度
  • bx =包围盒的宽度(红色)
  • by =边界框的高度(红色)
  • x =边界框的X坐标(红色)
  • y =边界框的Y坐标(红色)
  • iAngle / t = HTML元素的旋转角度(蓝色;未显示但是 在下面的代码中使用),仅供参考:在这个例子中它是37度(不是这个例子很重要)

如何通过JavaScript计算旋转的HTML元素(给定其宽度,高度和旋转角度)周围的边界框(所有红色数字)的X,Y,高度和宽度?一个棘手的问题是将旋转的HTML元素(蓝色框)的原始X / Y坐标用作某种方式的偏移(这在下面的代码中没有表示)。这可能需要查看CSS3's transform-origin以确定中心点。

我有一个部分解决方案,但X / Y坐标的计算功能不正常......

var boundingBox = function (iX, iY, iAngle) {
    var x, y, bx, by, t;

    //# Allow for negetive iAngle's that rotate counter clockwise while always ensuring iAngle's < 360
    t = ((iAngle < 0 ? 360 - iAngle : iAngle) % 360);

    //# Calculate the width (bx) and height (by) of the .boundingBox
    //#     NOTE: See https://stackoverflow.com/questions/3231176/how-to-get-size-of-a-rotated-rectangle
    bx = (iX * Math.sin(iAngle) + iY * Math.cos(iAngle));
    by = (iX * Math.cos(iAngle) + iY * Math.sin(iAngle));

    //# This part is wrong, as it's re-calculating the iX/iY of the rotated element (blue)
    //# we want the x/y of the bounding box (red)
    //#     NOTE: See https://stackoverflow.com/questions/9971230/calculate-rotated-rectangle-size-from-known-bounding-box-coordinates
    x = (1 / (Math.pow(Math.cos(t), 2) - Math.pow(Math.sin(t), 2))) * (bx * Math.cos(t) - by * Math.sin(t));
    y = (1 / (Math.pow(Math.cos(t), 2) - Math.pow(Math.sin(t), 2))) * (-bx * Math.sin(t) + by * Math.cos(t));

    //# Return an object to the caller representing the x/y and width/height of the calculated .boundingBox
    return {
        x: parseInt(x), width: parseInt(bx),
        y: parseInt(y), height: parseInt(by)
    }
};

我觉得自己如此接近,但到目前为止......

非常感谢您提供的任何帮助!

帮助非JAVASCRIPTERS ......

旋转HTML元素后,浏览器会返回“矩阵变换”或“旋转矩阵”,如下所示:rotate(Xdeg) = matrix(cos(X), sin(X), -sin(X), cos(X), 0, 0); See this page for more info

我有一种感觉,这将启发我们如何仅根据旋转元素的宽度,高度和角度(蓝色)获取边界框(红色)的X,Y。

新信息

嗯......很有意思......

enter image description here

每个浏览器似乎都以不同于X / Y的角度处理旋转! FF完全忽略它,IE&amp; Opera绘制边界框(但其属性未公开,即:bx&amp; by)和Chrome&amp; Safari旋转矩形!除FF外,所有都正确报告X / Y.所以......只有FF存在X / Y问题!多奇怪啊!

另外值得注意的是,$(document).ready(function () {...});似乎太早触发旋转的X / Y(这是我原来问题的一部分!)。我在$(document).ready(function () {...});中的X / Y询问调用之前直接旋转元素,但它们似乎直到(!?)之后的某个时间才更新。

当我多花一点时间的时候,我会用这个例子折腾jFiddle,但我正在使用“jquery-css-transform.js”的修改形式,所以我在jFiddle之前做了一点点的修修补补...

那么......什么事,FireFox?那不酷,伙计!

情节变浓......

好吧,FF12似乎解决了FF11的问题,现在就像IE和Opera一样。但是现在我回到了X / Y的方方面面,但至少我想我知道为什么现在......

似乎即使浏览器正确报告旋转对象的X / Y,在未旋转的版本上仍然存在“重影”X / Y.似乎这是操作的顺序:

  • 从未旋转的元素开始,X,Y为20,20
  • 旋转所述元素,导致X,Y报告为15,35
  • 通过JavaScript / CSS将所述元素移动到X,Y 10,10
  • 浏览器逻辑上将元素旋转回20,20,移动到10,10然后重新旋转,导致X,Y为5,25

所以...我希望元素在旋转后的10,10处结束,但是由于元素(看似)在移动后重新旋转,因此得到的X,Y与集合X不同,收率

这是我的问题!因此我真正需要的是获取所需目标坐标(10,10)的功能,并从那里向后工作以获得将导致元素旋转到10,10的起始X,Y坐标。至少我知道我现在的问题是什么,感谢浏览器的内部工作原理,似乎旋转元素10 = 5!

4 个答案:

答案 0 :(得分:14)

我知道这有点晚了,但我在HTML5画布上为这个问题编写了一个小提琴:

http://jsfiddle.net/oscarpalacious/ZdQKg/

我希望有人觉得它很有用!

我实际上并没有计算容器左上角的x,y。它是作为偏移的结果计算的(来自小提琴示例的代码):

this.w = Math.sin(this.angulo) * rotador.h + Math.cos(this.angulo) * rotador.w;
this.h = Math.sin(this.angulo) * rotador.w + Math.cos(this.angulo) * rotador.h;
// The offset on a canvas for the upper left corner (x, y) is
// given by the first two parameters for the rect() method:
contexto.rect(-(this.w/2), -(this.h/2), this.w, this.h);

干杯

答案 1 :(得分:5)

您是否尝试过使用 getBoundingClientRect()

此方法返回一个对象,当前值“bottom,height,left,right,top,width”考虑旋转

答案 2 :(得分:1)

将四个角从中心转为矢量,旋转它们,并从中获取新的最小/最大宽度/高度。

修改

我现在看到你遇到问题的地方。当您需要使用旋转中心的偏移量进行计算时,您正在使用整个侧面进行计算。是的,这导致四个旋转点(奇怪的是,这与你开始时的点数完全相同)。在他们之间将有一个最小X,一个最大X,一个最小Y和一个最大Y.这些是你的界限。

答案 3 :(得分:0)

My gist可以帮助您

多边形的边界框(矩形,三角形等):

现场演示https://jsfiddle.net/Kolosovsky/tdqv6pk2/

let points = [
    { x: 125, y: 50 },
    { x: 250, y: 65 },
    { x: 300, y: 125 },
    { x: 175, y: 175 },
    { x: 100, y: 125 },
];
let minX = Math.min(...points.map(point => point.x));
let minY = Math.min(...points.map(point => point.y));
let maxX = Math.max(...points.map(point => point.x));
let maxY = Math.max(...points.map(point => point.y));
let pivot = {
    x: maxX - ((maxX - minX) / 2),
    y: maxY - ((maxY - minY) / 2)
};
let degrees = 90;
let radians = degrees * (Math.PI / 180);
let cos = Math.cos(radians);
let sin = Math.sin(radians);

function rotatePoint(pivot, point, cos, sin) {
    return {
        x: (cos * (point.x - pivot.x)) - (sin * (point.y - pivot.y)) + pivot.x,
        y: (sin * (point.x - pivot.x)) + (cos * (point.y - pivot.y)) + pivot.y
    };
}

let boundingBox = {
    x1: Number.POSITIVE_INFINITY,
    y1: Number.POSITIVE_INFINITY,
    x2: Number.NEGATIVE_INFINITY,
    y2: Number.NEGATIVE_INFINITY,
};

points.forEach((point) => {
    let rotatedPoint = rotatePoint(pivot, point, cos, sin);

    boundingBox.x1 = Math.min(boundingBox.x1, rotatedPoint.x);
    boundingBox.y1 = Math.min(boundingBox.y1, rotatedPoint.y);
    boundingBox.x2 = Math.max(boundingBox.x2, rotatedPoint.x);
    boundingBox.y2 = Math.max(boundingBox.y2, rotatedPoint.y);
});

椭圆的边框:

现场演示https://jsfiddle.net/Kolosovsky/sLc7ynd1/

let centerX = 350;
let centerY = 100;
let radiusX = 100;
let radiusY = 50;
let degrees = 200;

let radians = degrees * (Math.PI / 180);
let radians90 = radians + Math.PI / 2;
let ux = radiusX * Math.cos(radians);
let uy = radiusX * Math.sin(radians);
let vx = radiusY * Math.cos(radians90);
let vy = radiusY * Math.sin(radians90);

let width = Math.sqrt(ux * ux + vx * vx) * 2;
let height = Math.sqrt(uy * uy + vy * vy) * 2;
let x = centerX - (width / 2);
let y = centerY - (height / 2);