html5 - 以相对坐标

时间:2016-04-15 05:51:52

标签: html5 mobile rotation device-orientation euler-angles

我尝试在左右轴和上下轴之间的两个deviceorientation事件之间进行方向更改,这些轴通常被定义为电话xy轴(https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Orientation_and_motion_data_explained

即在t1t2之间,这些电话轴从(x1, y1)移动到(x2, y2),它想要(angle(x2-x1), angle(y1-y2))。< / p>

当设备处于纵向模式时(与横向模式相反),这些轴似乎对应于betagamma。然而,当手机垂直(底部朝向地面)时,gamma值变得非常不稳定,并从90度跳到-90度(在同一时刻,alpha跳跃180度)你可以很容易地看到here on your phone

我想避免这种情况,并且还要获得360范围内的值。以下是我到目前为止的情况:

// assuming portrait mode
var beta0, gamma0;
window.addEventListener('deviceorientation', function(orientation) {
  if (typeof beta0 === 'undefined') {
    beta0 = beta;
    gamma0 = gamma;
  } 

  console.log('user has moved to the left by', gamma - gamma0, ' and to the top by', beta - beta0);
});

当设备大部分处于水平状态时可以正常工作,而当设备处于垂直状态时则完全无效

1 个答案:

答案 0 :(得分:6)

好的。首先,设备方向输入的简单说明:

绝对坐标系(X, Y, Z)使X为东,Y为北,Z为向上。设备相对坐标系(x, y, z)x是正确的,y是顶部,z是向上的。然后,方向角(alpha, beta, gamma)是描述三个简单旋转连续的角度,这些旋转将(X, Y, Z)更改为(x, y, z),如下所示:

  • 围绕Z alpha度旋转(X, Y, Z)度,(X', Y', Z') = Z'
  • Z转换为X'
  • 围绕beta (X', Y', Z')度旋转(X'', Y'', Z'')度,X'' = X'
  • Y''转换为gamma
  • 围绕(X'', Y'', Z'') (x, y, z)度旋转y度,Y'' = Z-X'-Y''
  • [ cC 0 sC ] [ 1 0 0 ] [ cA -sA 0 ] R(A, B, C) = Ry(C)*Rx(B)*Rz(A) = | 0 1 0 |*| 0 cB -sB |*[ sA cA 0 ] [ -sC 0 cC ] [ 0 sB cB ] [ 0 0 1 ] 转换为A, B, C

(它们被称为alpha, beta, gamma类型的内在Tait-Bryan角度

现在我们可以通过组合简单的旋转矩阵来获得相应的旋转矩阵,每个旋转矩阵对应于三个旋转中的一个。

s, c

其中sin, cosy的缩写,x的缩写为(x, y, z)

现在,我们感兴趣的是两个位置(x', y', z')和{{1}之间的左右((A, B, C)轴)和自上而下((A', B', C')轴)旋转增量的角度对应于方向(x', y', z')(x, y, z)

的}

R(A', B', C') * R(A, B, C)^-1 = R(A', B', C') * R(A, B, C)^T的{​​{1}}坐标由z' = p*x + q*y + r*z给出,因为逆是正交(旋转)矩阵的转置。最后,如果p,那些旋转的角度在左右轴周围为q,在自上而下的周围为asin(p)(对于小角度,这是假设频繁定向的情况)更新,否则asin(r)/* * gl-matrix is a nice library that handles rotation stuff efficiently * The 3x3 matrix is a 9 element array * such that indexes 0-2 correspond to the first column, 3-5 to the second column and 6-8 to the third */ import {mat3} from 'gl-matrix'; let _x, _y, _z; let cX, cY, cZ, sX, sY, sZ; /* * return the rotation matrix corresponding to the orientation angles */ const fromOrientation = function(out, alpha, beta, gamma) { _z = alpha; _x = beta; _y = gamma; cX = Math.cos( _x ); cY = Math.cos( _y ); cZ = Math.cos( _z ); sX = Math.sin( _x ); sY = Math.sin( _y ); sZ = Math.sin( _z ); out[0] = cZ * cY + sZ * sX * sY, // row 1, col 1 out[1] = cX * sZ, // row 2, col 1 out[2] = - cZ * sY + sZ * sX * cY , // row 3, col 1 out[3] = - cY * sZ + cZ * sX * sY, // row 1, col 2 out[4] = cZ * cX, // row 2, col 2 out[5] = sZ * sY + cZ * cY * sX, // row 3, col 2 out[6] = cX * sY, // row 1, col 3 out[7] = - sX, // row 2, col 3 out[8] = cX * cY // row 3, col 3 }; 更接近真相)

所以这里有一些javascript来获取旋转矩阵:

const deg2rad = Math.PI / 180; // Degree-to-Radian conversion
let currentRotMat, previousRotMat, inverseMat, relativeRotationDelta,
  totalRightAngularMovement=0, totalTopAngularMovement=0;

window.addEventListener('deviceorientation', ({alpha, beta, gamma}) => {
  // init values if necessary
  if (!previousRotMat) {
    previousRotMat = mat3.create();
    currentRotMat = mat3.create();
    relativeRotationDelta = mat3.create();

    fromOrientation(currentRotMat, alpha * deg2rad, beta * deg2rad, gamma * deg2rad);
  }

  // save last orientation
  mat3.copy(previousRotMat, currentRotMat);

  // get rotation in the previous orientation coordinate
  fromOrientation(currentRotMat, alpha * deg2rad, beta * deg2rad, gamma * deg2rad);
  mat3.transpose(inverseMat, previousRotMat); // for rotation matrix, inverse is transpose
  mat3.multiply(relativeRotationDelta, currentRotMat, inverseMat);

  // add the angular deltas to the cummulative rotation
  totalRightAngularMovement += Math.asin(relativeRotationDelta[6]) / deg2rad;
  totalTopAngularMovement += Math.asin(relativeRotationDelta[7]) / deg2rad;
}

现在我们得到了角度增量:

  _z = alpha;
  _x = beta;
  _y = gamma;

最后,考虑到屏幕方向,我们必须更换

const getScreenOrientation = () => {
  switch (window.screen.orientation || window.screen.mozOrientation) {
    case 'landscape-primary':
      return 90;
    case 'landscape-secondary':
      return -90;
    case 'portrait-secondary':
      return 180;
    case 'portrait-primary':
      return 0;
  }
  if (window.orientation !== undefined)
    return window.orientation;
};

const screenOrientation = getScreenOrientation();

_z = alpha;
if (screenOrientation === 90) {
  _x = - gamma;
  _y = beta;
}
else if (screenOrientation === -90) {
  _x = gamma;
  _y = - beta;
}
else if (screenOrientation === 180) {
  _x = - beta;
  _y = - gamma;
}
else if (screenOrientation === 0) {
  _x = beta;
  _y = gamma;
}

通过

static

请注意,累积的左右和上下角度将取决于用户选择的路径,并且无法直接从设备方向推断,但必须通过移动进行跟踪。您可以通过不同的动作到达相同的位置:

  • 方法1:

    • 保持手机水平并顺时针旋转90度。 (这既不是左右也不是上下旋转)
    • 让手机保持横向模式并向您旋转90度。 (这不是90度左右旋转)
    • 让手机朝向你并旋转90度以使其亮起。 (这不是90度左右旋转)
  • 方法2:

    • 将手机旋转90度,使其面向您并垂直(这是一个90度的上下旋转)