我编写了一个监听移动设备旋转的 JS SDK,提供 3 个输入:
α : Angular 范围在 0 到 360 度之间
β : 介于 -180 和 180 度之间的 Angular
γ:-90 到 90 度之间的 Angular
Documentation for device rotation
我曾尝试使用欧拉 Angular 来确定设备方向,但遇到了 gimbal lock effect ,当设备向上时,计算会爆炸。这导致我使用 Quaternion , 不会受到万向节锁定效应的影响。
我找到了 this js library将 α、β 和 γ 转换为四元数,因此对于以下值:
α:81.7324
β:74.8036
γ : -84.3221
我得到这个四元数的 ZXY 顺序:
w:0.7120695154301472
x:0.6893688637611577
y:-0.10864439143062626
z:0.07696733776346154
代码:
var rad = Math.PI / 180;
window.addEventListener("deviceorientation", function(ev) {
// Update the rotation object
var q = Quaternion.fromEuler(ev.alpha * rad, ev.beta * rad, ev.gamma * rad, 'ZXY');
// Set the CSS style to the element you want to rotate
elm.style.transform = "matrix3d(" + q.conjugate().toMatrix4() + ")";
}, true);
使用派生自四元数的 4d CSS 矩阵可视化设备方向反射(reflect)了正确的设备方向 (DEMO, use mobile):
错误使用 Euler Angels 和开发人员工具进行可视化 (DEMO, use mobile):
如果设备处于以下方向之一,我想编写一个获取 α、β 和 γ 并输出的方法:
- 肖像
- 肖像颠倒
- 左侧风景
- 横向右边
- 展示
- 向下显示
将每个方向定义为围绕相关轴的 +- 45° 范围。
我应该采取什么方法?
最佳答案
鉴于您已经设法将欧拉 Angular 转换为单位四元数,下面是确定设备方向的简单方法:
取一个笔直向上的世界空间向量(即沿 +z 轴)并使用四元数(或其共轭)将其旋转到设备坐标中。 (请注意,您也可以直接使用欧拉 Angular ,或使用旋转矩阵,或使用任何其他可应用于变换向量的设备旋转表示来执行此操作。)
获取转换后的向量,并找到绝对值最大的分量。这会告诉您设备的哪个轴最接近垂直方向,组件值的符号会告诉您它是向上还是向下。
特别是:
- 如果设备的 x 轴最垂直,则设备处于横向;
- 如果设备的 y 轴最垂直,则设备处于纵向;
- 如果设备的 z 轴是最垂直的,则设备的屏幕朝上或朝下。
这是一个简单的 JS 演示,它至少应该可以在 Chrome 上运行——或者它应该可以运行,除了设备方向 API 似乎根本无法在 Stack Snippets 上运行。 :( 对于现场演示,请尝试 this CodePen instead。
const orientations = [
['landscape left', 'landscape right'], // device x axis points up/down
['portrait', 'portrait upside down'], // device y axis points up/down
['display up', 'display down'], // device z axis points up/down
];
const rad = Math.PI / 180;
function onOrientationChange (ev) {
const q = Quaternion.fromEuler(ev.alpha * rad, ev.beta * rad, ev.gamma * rad, 'ZXY');
// transform an upward-pointing vector to device coordinates
const vec = q.conjugate().rotateVector([0, 0, 1]);
// find the axis with the largest absolute value
const [value, axis] = vec.reduce((acc, cur, idx) => (Math.abs(cur) < Math.abs(acc[0]) ? acc : [cur, idx]), [0, 0]);
const orientation = orientations[axis][1 * (value < 0)];
document.querySelector('#angles').textContent = `alpha = ${ev.alpha.toFixed(1)}°, beta = ${ev.beta.toFixed(1)}°, gamma = ${ev.gamma.toFixed(1)}°`;
document.querySelector('#vec').textContent = `vec = ${vec.map(a => a.toFixed(3))}, dominant axis = ${axis}, value = ${value.toFixed(3)}`;
document.querySelector('#orientation').textContent = `orientation = ${orientation}`;
}
onOrientationChange({ alpha: 0, beta: 0, gamma: 0 });
window.addEventListener("deviceorientation", onOrientationChange, true);
<script src="https://cdn.jsdelivr.net/npm/quaternion@1.1.0/quaternion.min.js"></script>
<div id="angles"></div>
<div id="vec"></div>
<div id="orientation"></div>
注意有 apparently设备方向 API 提供的欧拉 Angular 的符号和范围在浏览器之间存在一些不一致,可能导致在其他浏览器上计算出错误的符号。你可能需要做一些浏览器嗅探来解决这个问题,或者使用像 gyronorm.js 这样的包装库。 .
关于javascript - 使用四元数的设备方向,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56769428/