我已经编写了一个JS SDK,可以监听移动设备的旋转,并提供3个输入:
α:角度范围可以在0到360度之间
β:-180至180度之间的角度
γ:-90至90度之间的角度
Documentation for device rotation
我尝试使用欧拉角确定设备方向,但是遇到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矩阵可视化设备方向反映了正确的设备方向(DEMO, use mobile):
错误:使用Euler Angels和开发者工具(DEMO, use mobile)进行可视化:
如果设备处于以下方向之一,我想编写一个获取α,β和γ并输出的方法:
将每个方向定义为围绕相关轴的±45°范围。
我应该采取什么方法?
答案 0 :(得分:1)
鉴于您已经成功将欧拉角转换为单位四元数,这是一种确定设备方向的简单方法:
获取一个笔直向上(即沿+ 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>
请注意,在设备方向API提供的欧拉角的符号和范围内,浏览器之间存在apparently不一致,可能导致在其他浏览器上计算出错误的符号。您可能需要进行一些浏览器嗅探才能解决此问题,或者使用gyronorm.js之类的包装器库。