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

27
基本上我是在问这个问题与JavaScript有关:从旋转矩形计算边界框坐标

Diagram

在这种情况下:
  • iX = 旋转(蓝色)HTML元素的宽度
  • iY = 旋转(蓝色)HTML元素的高度
  • bx = 边界框(红色)的宽度
  • by = 边界框(红色)的高度
  • x = 边界框(红色)的X坐标
  • y = 边界框(红色)的Y坐标
  • iAngle/t = HTML元素(蓝色;未在下面的代码中显示,但用于代码中),旋转角度。FYI:在此示例中为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://dev59.com/x3A75IYBdhLWcg3wkJz1
    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://dev59.com/NGkw5IYBdhLWcg3wVpBi
    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)
    }
};

我觉得我离成功很近,但又似乎很远...
非常感谢您能提供的任何帮助!
为了帮助不会JavaScript的人...
一旦HTML元素被旋转,浏览器会返回一个“矩阵变换”或“旋转矩阵”,它似乎是这样的:rotate(Xdeg) = matrix(cos(X), sin(X), -sin(X), cos(X), 0, 0);请参阅此页面获取更多信息
我有一种感觉,这将启示我们如何仅基于旋转元素(蓝色)的宽度、高度和角度来获取边界框(红色)的X、Y坐标。
新资讯
嗯...有趣...

enter image description here

每个浏览器似乎都从X/Y角度处理旋转!FF完全忽略它,IE和Opera绘制边界框(但其属性未公开,即:bx和by),Chrome和Safari旋转矩形!所有浏览器都正确报告X/Y,除了FF。因此... X/Y问题似乎只存在于FF中!非常奇怪!此外,值得注意的是,似乎$(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,Y不同。
这是我的问题!所以我真正需要的是一个函数,它可以获取所需的目标坐标(10,10),并从那里向后计算出起始X、Y坐标,以使元素旋转到10,10。至少现在我知道了我的问题,因为由于浏览器的内部工作原理,似乎旋转的元素中的10等于5!

1
你能发布一个你想要的图表吗?图片胜过千言万语。 - Li-aung Yip
我赞同使用图片,特别是这样可以看到自己在做什么。 - Ignacio Vazquez-Abrams
谢谢!这张图确实必要(而不是依靠参考 Q 的图片)。 - Campbeln
你应该发布一张“之前”的照片以便于你自己。 - Ignacio Vazquez-Abrams
1
我的目标是从“后面”开始。 “前面”的话,蓝色方框将不会被旋转。由于CSS3的transform-origin属性,蓝色框可以从任何中心点进行旋转。请参见:http://www.w3schools.com/cssref/css3_pr_transform-origin.asp...当然,如果这使问题变得不可能,那么我会重新评估=) - Campbeln
请查看:https://dev59.com/1msz5IYBdhLWcg3w8MYb#30157405 ;) - abernier
4个回答

21

我知道这有点晚了,但是我已经为HTML5画布编写了一个fiddle来解决这个问题:

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

希望对某些人有用!

实际上,我没有计算容器左上角的 x,y坐标。 这是由偏移量计算得出的(取自fiddle示例代码):

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);

干杯


这太棒了!正是我在寻找的:D - docodemore
嗨,很酷,你如何适应处理未围绕其中心旋转的形状? - winthers
为了完整性,如果你的角度在0和2*PI之间,并且应用这个修正(来自小提琴):this.angulo = ((rotador.angulo > Math.PI * 0.5 && rotador.angulo < Math.PI * 1) || (rotador.angulo > Math.PI * 1.5 && rotador.angulo < Math.PI * 2))? Math.PI - rotador.angulo : rotador.angulo;,则此计算有效。 - rixo

6

你尝试过使用getBoundingClientRect()吗?

该方法返回一个对象,其中包含考虑旋转后的"bottom、height、left、right、top、width"的当前值。


2
该函数在大多数浏览器中不支持变换。请参见https://developer.mozilla.org/en-US/docs/DOM/element.getBoundingClientRect - Hoffmann
2019年-提高意识已不再是问题。请访问https://caniuse.com/#search=getBoundingClientRect()。 - user443917

1
将四个角从中心转换为向量,旋转它们,并从中获取新的最小/最大宽度/高度。
编辑:
我现在知道你遇到了什么问题。你正在使用整个边进行计算,而你需要使用从旋转中心的偏移量进行计算。是的,这会产生四个旋转点(奇怪的是,正好与你开始的点数一样)。它们之间将有一个最小X、一个最大X、一个最小Y和一个最大Y。那些就是你的边界。

谢谢你,但我对数学很蠢(它从来不喜欢我,我也很少喜欢它=)我相信你是正确的,但我需要手把手的指导。 - Campbeln
1
你不能随便旋转东西而不遇到一些三角函数。 ;) SOH CAH TOA! SOH CAH TOA! SOH... 一旦你需要用它来解决你关心的实际问题,而不是没有意义的课堂问题,学习起来就容易得多了。(例如:“Jones 农民有一根高度为5的旗杆,太阳与地面成36度角……影子有多长?”) - Li-aung Yip

0

我的GitHub 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);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接