使用四元数的设备方向

Shl*_*rtz 6 javascript mobile android ios sensors

我已经编写了一个JS SDK,可以监听移动设备的旋转,并提供3个输入:

?:角度可以在0到360度之间变化
吗?:-180至180度之间的角度
?:-90至90度之间的角度

设备旋转文档

我曾尝试使用Euler Angles确定设备方向,但遇到了万向节锁定效应,当设备指向上方时,该计算会爆炸。这导致我使用Quaternion,而不受万向节锁定效应的影响。

我已经找到了可转换?,?的js库。和?到四元数,因此对于以下值:

: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);
Run Code Online (Sandbox Code Playgroud)

使用从四元数派生的4d CSS矩阵可视化设备方向反映正确的设备方向(DEMO,使用mobile):
在此处输入图片说明


Euler Angels和开发人员工具(DEMO,使用移动设备)的可视化错误
在此处输入图片说明


我想写一个获取?,?的方法 和?并在设备处于以下方向之一时输出:

  • 肖像
  • 颠倒的肖像
  • 左景观
  • 景观正确
  • 显示
  • 显示下来

将每个方向定义为围绕相关轴的±45°范围内。

我应该采取什么方法?

Ilm*_*nen 4

鉴于您已经成功地将欧拉角转换为单位四元数,这里有一个确定设备方向的简单方法:

\n
    \n
  1. 取一个垂直向上的世界空间向量(即沿着 + z轴)并使用四元数(或其共轭)将其旋转到设备坐标中。(请注意,您还可以直接使用欧拉角、使用旋转矩阵或使用可应用于变换矢量的设备旋转的任何其他表示来执行此操作。)

    \n
  2. \n
  3. 取出变换后的向量,找到绝对值最大的分量。这将告诉您设备的哪个轴指向最接近垂直方向,并且组件值的符号告诉您它是指向上方还是下方。

    \n
  4. \n
\n

尤其:

\n
    \n
  • 如果设备x轴最垂直,则设备处于横向方向;
  • \n
  • 如果设备y轴最垂直,则设备处于纵向方向;
  • \n
  • 如果设备的z轴最垂直,则设备的屏幕朝上或朝下。
  • \n
\n

这是一个简单的 JS 演示,至少应该在 Chrome \xe2\x80\x94 上工作,或者它会,但设备方向 API 似乎根本无法在 Stack Snippets 中工作。:( 对于现场演示,请尝试使用 CodePen

\n

\r\n
\r\n
const orientations = [\n  [\'landscape left\', \'landscape right\'],  // device x axis points up/down\n  [\'portrait\', \'portrait upside down\'],   // device y axis points up/down\n  [\'display up\', \'display down\'],         // device z axis points up/down\n];\n\nconst rad = Math.PI / 180;\n\nfunction onOrientationChange (ev) {\n  const q = Quaternion.fromEuler(ev.alpha * rad, ev.beta * rad, ev.gamma * rad, \'ZXY\');\n\n  // transform an upward-pointing vector to device coordinates\n  const vec = q.conjugate().rotateVector([0, 0, 1]);\n\n  // find the axis with the largest absolute value\n  const [value, axis] = vec.reduce((acc, cur, idx) => (Math.abs(cur) < Math.abs(acc[0]) ? acc : [cur, idx]), [0, 0]);\n\n  const orientation = orientations[axis][1 * (value < 0)];\n\n  document.querySelector(\'#angles\').textContent = `alpha = ${ev.alpha.toFixed(1)}\xc2\xb0, beta = ${ev.beta.toFixed(1)}\xc2\xb0, gamma = ${ev.gamma.toFixed(1)}\xc2\xb0`;\n  document.querySelector(\'#vec\').textContent = `vec = ${vec.map(a => a.toFixed(3))}, dominant axis = ${axis}, value = ${value.toFixed(3)}`;\n  document.querySelector(\'#orientation\').textContent = `orientation = ${orientation}`;\n}\n\nonOrientationChange({ alpha: 0, beta: 0, gamma: 0 });\nwindow.addEventListener("deviceorientation", onOrientationChange, true);
Run Code Online (Sandbox Code Playgroud)\r\n
<script src="https://cdn.jsdelivr.net/npm/quaternion@1.1.0/quaternion.min.js"></script>\n<div id="angles"></div>\n<div id="vec"></div>\n<div id="orientation"></div>
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

请注意,设备方向 API 提供的欧拉角的符号和范围在浏览器之间显然存在一些不一致,这可能会导致在其他浏览器上计算出错误的符号。您可能需要进行一些浏览器嗅探来解决此问题,或者使用像gyronorm.js这样的包装器库。

\n