2016-04-15 70 views
5

我試圖獲取沿左右軸和上下軸的兩個事件之間的方向變化,這些軸通常定義爲手機xy軸瞬間t1t2其中那些電話線從(x1, y1)移動到(x2, y2)之間(https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Orientation_and_motion_data_explainedhtml5 - 在相對座標系下獲取設備方向旋轉

即,它想獲得(angle(x2-x1), angle(y1-y2))

當設備處於縱向模式(與橫向模式相反)時,這些軸似乎對應於betagamma。然而,當手機處於垂直(底面向地面)時,gamma值變得非常不穩定,和90至-90度跳躍(在相同的場合,α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); 
}); 

當設備大多是水平的,而不是在所有當它是垂直

回答

3

好的工作正常。首先,設備定向輸入一個簡單的解釋:

絕對座標系,(X, Y, Z)是這樣的:X是東,Y是北美和Z到了。設備相對座標系(x, y, z)就是這樣的,x是正確的,y是最高的,z是最高的。然後,定向角,(alpha, beta, gamma)是描述的該改變(X, Y, Z)(x, y, z)作爲這樣三個簡單的旋轉繼承角度:

  • alpha度,其將(X, Y, Z)(X', Y', Z')與繞ZZ' = 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, gammas, c和用於sin, cos

現在,我們感興趣的是左右(y軸)和自上而下(x軸)的兩個位置(x, y, z)(x', y', z')之間旋轉增量的對應於所述取向角度(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給出,因爲逆是正交(旋轉)矩陣的轉置。最後,如果是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 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; 
} 

注意,累積左右和上下角度將取決於用戶所選擇的路徑,並且不能從設備方向直接推斷,而必須通過運動進行跟蹤。您可以到達同一位置不同的走勢:

  • 方法1:

    • 保持手機水平和順時針旋轉90度。 (這既不是左右旋轉也不是上下旋轉)
    • 讓您的手機處於橫向模式並向您旋轉90°。 (這不是90度的左右旋轉)
    • 讓你的手機朝向你,並旋轉90°,這樣它就起來了。 (這既不是左右一旋轉90度)
  • 方法2:

    • 90度旋轉手機以使其朝向你和垂直(這是一個90度的頂底部旋轉)
+0

我嘗試推行這種方法 - 請https://stackoverflow.com/questions/46975452/adjusting-mobile-accelerometer-data-to-account-for-phone-rotation - 我我得到不同的acc。我打開手機時的結果。你能評論一下,看看我是否錯過了一些東西?謝謝 – user1361529