問題描述
我試圖在兩個 deviceorientation
事件之間沿左右軸和上下軸改變方向,這些軸通常定義為電話 x
和 y
軸(https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Orientation_and_motion_data_explained)
I'm trying to get the change in orientation between two deviceorientation
events along the left-right axis, and top-bottom axis, those axis being usually defined as the phone x
and y
axis (https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Orientation_and_motion_data_explained)
即在瞬間 t1
和 t2
之間,這些音素軸從 (x1, y1)
移動到 (x2, y2)
,想得到(angle(x2-x1), angle(y1-y2))
.
ie between instants t1
and t2
where those phone axis move from (x1, y1)
to (x2, y2)
, It'd like to get (angle(x2-x1), angle(y1-y2))
.
當設備處于縱向模式(與橫向模式相反)時,這些軸似乎對應于 beta
和 gamma
.但是當手機垂直(底面朝地)時,gamma
值變得極不穩定,從90度跳到-90度(同時alpha跳180度)你可以在您的手機上輕松查看此處
When the device is in portrait mode (in opposition to landscape mode), those axis seems to correspond to the beta
and gamma
. However when the phone is vertical (bottom facing the ground), the gamma
value becomes extremely instable, and jumps from 90 to -90 degrees (at the same occasion, the alpha jumps by 180 degrees) You can easily see that here on your phone
我想避免這種情況,并獲得 360 范圍內的值.這是我目前所擁有的:
I'd like to avoid that, and also get values in the 360 range. Here is what I have so far:
// 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);
});
當設備大部分是水平的時候可以正常工作,而當它是垂直的時候就不行了
That works ok when the device is mostly horizontal, and not at all when it is vertical
推薦答案
好的.先簡單解釋一下設備方向輸入:
All right. First, a simple explanation of the device orientation input:
絕對坐標系,(X, Y, Z)
使得X
為東,Y
為北,Z
已啟動.設備相對坐標系,(x, y, z)
使得x
是對的,y
是頂部,z
已啟動.然后方向角,(alpha, beta, gamma)
是描述三個簡單旋轉連續的角度,這些旋轉將 (X, Y, Z)
更改為 (x, y, z)
如下:
The absolute coordinate system, (X, Y, Z)
is such that X
is East, Y
is North and Z
is up. The device relative coordinate system, (x, y, z)
is such that x
is right, y
is top and z
is up. Then the orientation angles, (alpha, beta, gamma)
are the angles that describe the succession of three simple rotations that change (X, Y, Z)
to (x, y, z)
as so:
- 圍繞
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) withy
=Y''
- rotate around
Z
byalpha
degrees, which transforms(X, Y, Z)
to(X', Y', Z')
withZ'
=Z
- rotate around
X'
bybeta
degrees, which transforms(X', Y', Z')
to(X'', Y'', Z'')
withX''
=X'
- rotate around
Y''
bygamma
degrees, which transforms(X'', Y'', Z'')
to(x, y, z)
withy
=Y''
(它們被稱為 Z-X'-Y''
類型的固有 Tait-Bryan 角)
(they are called intrinsic Tait-Bryan angles of type Z-X'-Y''
)
現在我們可以通過組合簡單的旋轉矩陣得到對應的旋轉矩陣,每個旋轉矩陣對應三個旋轉之一.
Now we can get the corresponding rotation matrix by composing simple rotation matrix that each correspond to one of the three rotations.
[ 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
的縮寫,s, c
是 sin, cos代碼>.
where A, B, C
are short for alpha, beta, gamma
and s, c
for sin, cos
.
現在,我們感興趣的是左右(y
軸)和自上而下(x
軸)旋轉的角度在兩個位置之間的增量(x, y, z)
和 (x', y', z')
對應于方向 (A, B, C)
和 <代碼>(A', B', C')
Now, we are interested in the angles of the right-left (y
axis) and top-down (x
axis) rotations deltas between two positions (x, y, z)
and (x', y', z')
that correspond to the orientations (A, B, C)
and (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)
更接近說實話)
The coordinates of (x', y', z')
in term of (x, y, z)
are given by R(A', B', C') * R(A, B, C)^-1 = R(A', B', C') * R(A, B, C)^T
since the inverse is the transpose for orthogonal (rotation) matrix. Finally, if z' = p*x + q*y + r*z
, the angle of those rotations are p
around the right-left axis and q
around the top-down one (this is true for small angles, which assume frequent orientation update, else asin(p)
and asin(r)
are closer from the truth)
所以這里有一些 javascript 來獲取旋轉矩陣:
So here is some javascript to get the rotation matrix:
/*
* 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
};
現在我們得到了角度增量:
and now we get the angular deltas:
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;
}
最后,考慮到屏幕方向,我們必須替換
Finally, to account for screen orientation, we have to replace
_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;
}
請注意,累積的左右和上下角度取決于用戶選擇的路徑,不能直接從設備方向推斷,而是必須通過移動進行跟蹤.您可以通過不同的動作到達相同的位置:
Note that the cumulative right-left and top-bottom angles will depend of the path chosen by the user, and cannot be infer directly from the device orientation but have to be tracked through the movement. You can arrive to the same position with different movements:
方法一:
method 1:
- 保持手機水平并順時針旋轉 90 度.(這既不是左右旋轉也不是上下旋轉)
- 讓您的手機保持橫向模式,然后朝您的方向旋轉 90 度.(這既不是 90 度左右旋轉)
- 讓您的手機面向您并旋轉 90 度以使其向上.(這既不是 90 度左右旋轉)
方法二:
- 將手機旋轉 90 度,使其面向您并且垂直(這是 90 度上下旋轉)
這篇關于html5 - 以相對坐標獲取設備方向旋轉的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!