【问题标题】:html5 - Get device orientation rotation in relative coordinatehtml5 - 以相对坐标获取设备方向旋转
【发布时间】:2016-04-15 05:51:51
【问题描述】:

我试图让两个deviceorientation 事件沿左右轴和上下轴之间的方向变化,这些轴通常定义为电话xy 轴(@ 987654321@)

即在瞬间t1t2 之间,那些电话轴从(x1, y1) 移动到(x2, y2),它想得到(angle(x2-x1), angle(y1-y2))

当设备处于纵向模式(与横向模式相反)时,这些轴似乎对应于betagamma。但是当手机垂直(底部朝向地面)时,gamma 值变得非常不稳定,从 90 度跳到 -90 度(同时,alpha 跳了 180 度)你可以很容易地看到 @987654322 @

我想避免这种情况,并获得 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);
});

当设备大部分是水平的时候可以正常工作,而当它是垂直的时候就不行了

【问题讨论】:

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


    【解决方案1】:

    好的。先简单解释一下设备方向输入:

    绝对坐标系(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'' 类型的固有 Tait-Bryan 角)

    现在我们可以通过组合简单的旋转矩阵得到对应的旋转矩阵,每个旋转矩阵对应三个旋转之一。

                                     [   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, Calpha, beta, gamma 的缩写,s, csin, cos 的缩写。

    现在,我们对与方向 @ 对应的两个位置 (x, y, z)(x', y', z') 之间的左右(y 轴)和自上而下(x 轴)旋转的角度感兴趣987654361@和(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 给出,因为逆矩阵是正交(旋转)矩阵的转置。最后,如果z' = p*x + q*y + r*z,则这些旋转的角度是p 围绕左右轴,q 围绕自上而下的轴(对于假设频繁方向更新的小角度来说是这样,否则asin(p)asin(r) 更接近真相)

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

    /*
     * 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
    };
    

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

    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 screen = window.screen;
    const getScreenOrientation = () => {
      const oriented = screen && (screen.orientation || screen.mozOrientation);
      if (oriented) switch (oriented.type || oriented) {
        case 'landscape-primary':
          return 90;
        case 'landscape-secondary':
          return -90;
        case 'portrait-secondary':
          return 180;
        case 'portrait-primary':
          return 0;
      }
      return window.orientation|0; // defaults to zero if orientation is unsupported
    };
    
    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;
    }
    

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

    • 方法一:

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

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

    【讨论】:

    猜你喜欢
    • 2014-04-21
    • 2020-11-20
    • 1970-01-01
    • 2018-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-10
    • 1970-01-01
    相关资源
    最近更新 更多