我有一个3x3矩阵(startMatrix),它代表图像的实际视图(平移,旋转和缩放)。现在,我创建一个具有单位矩阵,新的x和y坐标,新的角度和新的比例的新矩阵(endMatrix),例如:
endMatrix = translate(identityMatrix, -x, -y);
endMatrix = rotate(endMatrix, angle);
endMatrix = scale(endMatrix, scale);
endMatrix = translate(endMatrix,(screen.width/2)/scale,screen.height/2)/scale);
和功能(标准的东西)
function scale(m,s) {
var n = new Matrix([
[s, 0, 0],
[0, s, 0],
[0, 0, s]
]);
return n.multiply(m);
}
function rotate(m, theta) {
var n = new Matrix([
[Math.cos(theta), -Math.sin(theta), 0],
[Math.sin(theta), Math.cos(theta), 0],
[0, 0, 1]
]);
return n.multiply(m);
}
function translate(m, x, y) {
var n = new Matrix([
[1, 0, x],
[0, 1, y],
[0, 0, 1]
]);
return n.multiply(m);
}
之后,我使用css转换matrix3d(仅用于硬件加速的3d)来转换图像。此转换使用requestAnimationFrame进行动画处理。
以我的startMatrix为例
和endMatrix
线性组合如下所示:
t从0到1
变换矩阵的线性组合(最终的图像位置)的结果是正确的,我现在的问题是:如果新角度与实际角度相差约180度,则endMatrix值将从正变为负(或以其他方式改变)大约)。这会在转换后的图像的动画中产生放大缩小效果。
有没有办法最好地使用一个矩阵进行转换来防止这种情况?
如果直接插值矩阵值,将会出现问题,对于很小的角度,无法观察到误差,但是从长远来看,您将面临问题。即使将矩阵归一化,较大的角度也会使问题在视觉上显而易见。
2D旋转非常简单,因此无需旋转矩阵方法就可以做得很好。最好的方法可能是使用四元数,但是也许更适合3D转换。
采取的步骤是:
在动画的开始,您必须从步骤1计算一次值,然后在每个帧上应用步骤2和3。
第1步:获取旋转,缩放,变换
假设开始矩阵为S,结束矩阵为E。
转换值只是它们的最后一列,例如
var start_tx = S[0][2];
var start_ty = S[1][2];
var end_tx = E[0][2];
var end_ty = E[1][2];
例如,非倾斜2D矩阵的标度只是该矩阵所跨越的空间中基矢量之一的长度。
// scale is just the length of the rotation matrixes vector
var startScale = Math.sqrt( S[0][0]*S[0][0] + S[1][0]*S[1][0]);
var endScale = Math.sqrt( E[0][0]*E[0][0] + E[1][0]*E[1][0]);
最难的部分是获取矩阵的旋转值。好处是每个插值只需要计算一次。
可以基于矩阵列正在创建的向量之间的角度来计算两个2D矩阵的旋转角度。如果没有旋转,则第一列的值(1,0)代表x轴,第二列的值(0,1)代表y轴。
通常,矩阵S的x轴位置表示为
(S [0] [0],S [0] [1])
y轴指向方向
(S [1] [0],S [1] [1])
对于任何2D 3x3矩阵也是如此,例如E。
使用此信息,您可以仅使用标准矢量数学来确定两个矩阵之间的旋转角度-如果我们假设不存在任何倾斜。
// normalize column vectors
var s00 = S[0][0]/ startScale; // x-component
var s01 = S[0][1]/ startScale; // y-component
var e00 = E[0][0]/ endScale; // x-component
var e01 = E[0][1]/ endScale; // y-component
// calculate dot product which is the cos of the angle
var dp_start = s00*1 + s01*0; // base rotation, dot prod against x-axis
var dp_between = s00*e00 + s01*e01; // between matrices
var startRotation = Math.acos( dp_start );
var deltaRotation = Math.acos( dp_between );
// if detect clockwise rotation, the y -comp of x-axis < 0
if(S[0][1]<0) startRotation = -1*startRotation;
// for the delta rotation calculate cross product
var cp_between = s00*e01 - s01*e00;
if(cp_between<0) deltaRotation = deltaRotation*-1;
var endRotation = startRotation + deltaRotation;
在这里,startRotation仅根据矩阵的第一个值的acos计算。但是,第二列的第一个值-sin(angle)大于零,则矩阵已顺时针旋转,并且角度必须为负。之所以必须这样做,是因为acos仅给出正值。
另一种考虑方式是叉积s00 * e01-s01 * e00,其中起始位置(s00,s01)是x轴,其中s00 == 1和s01 == 0,终点(e00,e01)是(S [0] [0],S [0] [1])创建叉积
1 * S[0][1] - 0 * S[0][0]
这是S [0] [1]。如果该值为负,则x轴已转向顺时针方向。
对于endRotation,我们需要从S到E的增量旋转。这可以类似地从矩阵跨越的向量之间的点积计算得出。同样,我们测试叉积,以查看旋转方向是否为顺时针方向(负角)。
步骤2:内插
在动画期间,获取新值很简单:
var scale = startScale + t*(endScale-startScale);
var rotation = startRotation + t*(endRotation-startRotation);
var tx = start_tx + t*(end_tx-start_tx);
var ty = start_ty + t*(end_ty-start_ty);
步骤3构造矩阵
对于每一帧构造最终矩阵,您只需要将值放入转换矩阵矩阵中
var cs = Math.cos(rotation);
var sn = Math.sin(rotation);
var matrix_values = [[scale*cs, -scale*sn, tx], [scale*sn, scale*cs, ty], [0,0,1]]
然后,您将拥有一个2D矩阵,该矩阵也可以轻松地添加到任何3D硬件加速器中。
免责声明:某些代码已经过测试,某些代码尚未过测试,因此很可能会发现错误。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句