javascript - 使用四元数的设备方向

标签 javascript android ios mobile sensors

我编写了一个监听移动设备旋转的 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):
enter image description here


错误使用 Euler Angels 和开发人员工具进行可视化 (DEMO, use mobile):
enter image description here


如果设备处于以下方向之一,我想编写一个获取 α、β 和 γ 并输出的方法:

  • 肖像
  • 肖像颠倒
  • 左侧风景
  • 横向右边
  • 展示
  • 向下显示

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

我应该采取什么方法?

最佳答案

鉴于您已经设法将欧拉 Angular 转换为单位四元数,下面是确定设备方向的简单方法:

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

  2. 获取转换后的向量,并找到绝对值最大的分量。这会告诉您设备的哪个轴最接近垂直方向,组件值的符号会告诉您它是向上还是向下。

特别是:

  • 如果设备的 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/

相关文章:

javascript - 将值(value)从焦点传递到模糊功能的最佳方式?

javascript - 导航使两个 div 滚动

android - 如何在Android中处理RecyclerView.Adapter的SELECTION?

android - 使用 Universal Image Loader Android 在 ListView 中加载错误的图像?

iphone - UIView 动画完成 block 在其他动画开始时触发

Android App : can I use iOS sqlite? 如果没有,我如何从云 (Parse.com) 中的数据库创建本地数据库?

javascript - 传单tilelayer显示图像未找到丢失瓷砖的图标

javascript - 为什么我的对象 contentDocument 返回 null?

android - Unity 2019.2.0f1 无法定位Android NDK

ios - 在 swift 上从另一个 View Controller 更改标签