来自 aditya 的 评论
有没有办法 [通过 JavaScript] 获取元素旋转的角度?
这似乎是一个合理的要求。 因此我们有一些 HTML
<div id="i-am-rotated">text</div>
它通过 CSS 旋转
#i-am-rotated {
-webkit-transform: rotate(30deg);
-moz-transform: rotate(30deg);
-ms-transform: rotate(30deg);
-o-transform: rotate(30deg);
}
我们的目标是通过 JavaScript 从该元素中获取数字“30”。 从元素访问样式信息的现代方法是 getComputedStyle()
(在所有现代浏览器和 IE 9+ 中支持,较旧的 IE 支持 currentStyle()
)。 让我们尝试使用 getComputedStyle() 获取它
var el = document.getElementById("i-am-rotated");
var st = window.getComputedStyle(el, null);
var tr = st.getPropertyValue("-webkit-transform") ||
st.getPropertyValue("-moz-transform") ||
st.getPropertyValue("-ms-transform") ||
st.getPropertyValue("-o-transform") ||
st.getPropertyValue("transform") ||
"Either no transform set, or browser doesn't do getComputedStyle";
您可能认为返回值将是“rotate(30deg)”,我们可以在其上运行 parseInt()
并获得“30”。 但不幸的是,这行不通。 我们实际获得的值如下
console.log(tr);
// matrix(0.8660254037844387, 0.49999999999999994, -0.49999999999999994, 0.8660254037844387, 0, 0)
浏览器将 CSS 旋转转换转换为矩阵转换。 我想它是这样做的,以简化对单个元素的多个转换成为一个值。 那么我们该怎么办呢?
Nicolas Gallager 研究 了旋转转换的矩阵转换。 从本质上讲就是 这个
rotate(Xdeg) = matrix(cos(X), sin(X), -sin(X), cos(X), 0, 0);
我们实际上只需要其中之一来进行快速计算。 我们需要获取这些值的反正弦(正弦的倒数,sin-1),确保以弧度获取。
首先,我们将获取单独的矩阵值
// UPDATE: below was causing errors sometimes...
// var values = tr.split('(')[1].split(')')[0].split(',');
// Replace with... (thanks Thierry)
var values = tr.split('(')[1],
values = values.split(')')[0],
values = values.split(',');
var a = values[0]; // 0.866025
var b = values[1]; // 0.5
var c = values[2]; // -0.5
var d = values[3]; // 0.866025
然后我们知道 sin(X) == 0.5
所以 asin(0.5) == radians
并且 degrees == radians * 180/π
。
所以
var angle = Math.round(Math.asin(b) * (180/Math.PI));
console.log(angle);
// 30
太好了!
Nicolas 通过考虑比例进一步进行了研究。 使用以下完整代码,可以提取旋转值,无论应用了多少其他转换。
#complex-transform {
-webkit-transform: rotate(30deg) scale(1.2) skew(10deg) translate(5px, 5px);
-moz-transform: rotate(30deg) scale(1.2) skew(10deg) translate(5px, 5px);
-ms-transform: rotate(30deg) scale(1.2) skew(10deg) translate(5px, 5px);
-o-transform: rotate(30deg) scale(1.2) skew(10deg) translate(5px, 5px);
}
var el = document.getElementById("complex-transform");
var st = window.getComputedStyle(el, null);
var tr = st.getPropertyValue("-webkit-transform") ||
st.getPropertyValue("-moz-transform") ||
st.getPropertyValue("-ms-transform") ||
st.getPropertyValue("-o-transform") ||
st.getPropertyValue("transform") ||
"fail...";
// With rotate(30deg)...
// matrix(0.866025, 0.5, -0.5, 0.866025, 0px, 0px)
console.log('Matrix: ' + tr);
// rotation matrix - http://en.wikipedia.org/wiki/Rotation_matrix
var values = tr.split('(')[1];
values = values.split(')')[0];
values = values.split(',');
var a = values[0];
var b = values[1];
var c = values[2];
var d = values[3];
var scale = Math.sqrt(a*a + b*b);
// arc sin, convert from radians to degrees, round
// DO NOT USE: see update below
var sin = b/scale;
var angle = Math.round(Math.asin(sin) * (180/Math.PI));
// works!
console.log('Rotate: ' + angle + 'deg');
更新:事实证明,此行对于计算角度更可靠
var angle = Math.round(Math.atan2(b, a) * (180/Math.PI));
太好了!
scale() 和 rotation() 正常! 但是……
skew() 和 translate() 怎么样?
skew()
(X 或 Y)转换仍将影响用于表示旋转的 2×2 矩阵,因此要考虑倾斜,您需要进行进一步的计算。 Christian Schaefer 在规范中发现了这一点:http://www.w3.org/TR/2011/WD-css3-2d-transforms-20111215/#matrix-decomposition我可能永远不会使用它,但了解计算机使用矩阵来计算旋转,而不是弧度或度数或其他东西,这很有趣。
不幸的是,对其中一个值进行反正弦运算是不够的,因为它会导致歧义。
我将跳过关于反转非单射周期函数的枯燥部分(此评论框太小); 只要看看
因此,反正弦不足以区分两者。
您还需要余弦的信息:如果它是正数,则没问题,如果它是负数,则必须取 180°-反正弦(b)
将其归档到“我在现实生活中永远不需要这个”;)
我认为计算中缺少了一些东西。
我修改了小提琴,使其在(然后将其移动了一些,以便您可以看到它)上进行 130 度旋转,脚本报告了 50 度旋转
http://jsfiddle.net/459jS/98/
很棒的发现!
看起来,考虑到 Nicolas 在上面链接的新数学公式,我们可以将反正弦运算更改为
它对所有我尝试过的角度都返回了良好的结果,包括 Agos 在上面提出的问题。
嘿,任何查看此内容的人可能对 Easy Transform 感兴趣。 它远没有它可能那么强大或有用。 如果可以,请对其进行分叉并添加,或者只是提供一般反馈和使用情况会很棒
Easy Transform @ Github
啊,我记得在 Java3D 中做过类似的事情 - 将矩阵转换回旋转角度。
在 Firefox 和 Opera 中它是否相同?
太棒了
数学真是太有趣了 :)
很棒的文章,Chris,还有很棒的分析,Nicolas。
哇,这真的需要成为一个 jQuery 方法。
我不知道我已经等待了多久,但已经有一段时间了。
我可能需要一段时间。
谢谢
很棒的文章! 感谢您的建议,非常感谢!
嗨,这可能只是我,但为什么我们不能只使用
或类似的东西,在我的 Chrome 控制台中,它返回
我在这里错过了什么吗?
当需要获取大于 180 度的旋转值时,我遇到了此代码的问题。
事实证明,
atan2
函数只返回 -pi 到 pi 之间的值,因此您只获得 -180 到 180 的度数值。 例如,当我尝试获取 270 度的旋转时,代码返回 -90。唯一的改变是添加一个检查,看看以弧度表示的结果是否小于零,如果是,则在结果中添加
(2 * pi)
请参阅我的笔,了解一下比较:http://codepen.io/jjeaton/pen/bzolH
谢谢!! :D
我认为示例略有错误(不是针对主要文章内容,而是针对比例内容),而且有一些随机代码在运行!
然后,据我所知(它很少/数学上有记录),矩阵的工作方式如下
因此,接下来的部分变得非常令人困惑(最后一个示例)
我会写成
首先,它阻止我们只将 values[0]、values[1]、values[2] 等映射到同样无用的 a、b、c,但重要的是它向我们表明
var scale = Math.sqrt(a*a + b*b);
行可以简单地删除,因为我们已经在每个维度上都有 scale。 此外,如果我们使用该示例,则 scale = sqrt((0.866025 * 0.866025) + (0.5 * 0.5)) =0.9999996503
但实际上,我们知道矩阵本身表明 scale 在 x 上是 0.8660254037844387,在 y 上是 0.8660254037844387,所以我想这个数字应该读作 0.8660254037844387。(除非我太愚蠢,否则该方程使用的方程使用不正确的点找到三角形的斜边!)
刚开始学习这些,所以我希望自己是对的,如果错了,我非常尴尬。 如果你把它写进文章,可以随意删除这条评论:)
谢谢!! 你救了我几个小时的脑力劳动!
谢谢! 你救了我几个小时的脑力劳动!