我有一个3x3矩阵(startMatrix),它代表图像的实际视图(平移,旋转和缩放) . 现在我创建一个新的矩阵(endMatrix),其中包含一个identitymatrix,新的x和y坐标,新的角度和新的比例,如:
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 transform matrix3d转换图像(3d仅用于硬件加速) . 此变换使用requestAnimationFrame进行动画处理 .
我的startMatrix就是例如
和endMatrix
线性组合看起来像:
t从0到1
变换矩阵的线性组合(得到的图像位置)的结果是正确的,我现在的问题是:如果新角度与实际角度相差大约180度,则endMatrix值从正变为负(或者反之)周围) . 这导致变换图像的动画中的放大缩小效果 .
有没有办法防止这种情况,最好使用一个矩阵进行转换?
4 回答
当一个动画旋转保留比例,点不沿直线移动,而是沿圆圈移动 . 结果,中间矩阵不是起始矩阵和末端矩阵的线性组合 . 克服这个问题的最简单方法是计算每个动画帧期间的所有变换:
如果直接插值矩阵值会出现问题,对于非常小的角度,不能观察到不准确,但从长时间来看,您将遇到问题 . 即使你对矩阵进行归一化,更大的角度也会使问题在视觉上变得明显 .
2D旋转非常简单,因此无需旋转矩阵方法即可完成 . 最好的方法可能是使用四元数,但它们可能更适合3D转换 .
要采取的步骤是:
计算旋转,缩放和变换值 . 如果您已经拥有这些,则可以跳过此步骤 . 对于2D矩阵变换,保持这些值是分开的可能是最容易的 .
然后应用插值到这些值
根据计算构造新矩阵
在动画开始时,您必须计算步骤1中的值一次,然后在每帧处应用步骤2和3 .
Step 1: get the rotation, scale, transform
假设起始矩阵为S且结束矩阵为E.
例如,转换值只是最后一列
例如,非倾斜2D矩阵的尺度仅仅是矩阵跨越的空间中的任一个基矢量的长度
最难的部分是获得矩阵的旋转值 . 好处是每次插值只需要计算一次 .
可以基于矩阵列创建的矢量之间的角度来计算两个2D矩阵的旋转角度 . 如果没有旋转,则第一列具有表示x轴的值(1,0),第二列具有表示y轴的值(0,1) .
通常,矩阵S的x轴位置由下式表示
并且y轴指向方向
对于任何2D 3x3矩阵都是一样的,比如E.
使用此信息,您可以仅使用标准矢量数学来确定两个矩阵之间的旋转角度 - 如果我们假设没有偏斜 .
这里 startRotation 仅根据矩阵的第一个值的acos计算 . 但是,第二列第一个值,即 -sin(angle) 大于零,则矩阵已顺时针旋转,角度必须为负 . 必须这样做,因为acos只给出正值 .
另一种考虑方法是考虑交叉乘积s00 * e01 - s01 * e00,其中起始位置(s00,s01)是x轴,其中s00 == 1和s01 == 0,结束(e00,e01)是(S [0] [0],S [0] [1])创建叉积
这是S [0] [1] . 如果该值为负,则x轴已转为顺时针方向 .
对于 endRotation ,我们需要从S到E的Δ旋转 . 这可以类似地从矩阵跨越的矢量之间的点积计算 . 类似地,我们测试交叉乘积以查看旋转方向是否是顺时针(负角度) .
Step 2: interpolate
在动画期间获取新值是微不足道的插值:
Step 3 construct the matrix
对于每个帧构造最终矩阵,您只需将值放入变换矩阵矩阵中
然后你有一个2D矩阵,它也很容易为任何3D硬件加速器提供 .
免责声明:部分代码已经过测试,其中一些尚未经过测试,因此最有可能发现错误 .
使用JavaScript计算转换实际上绕过了硬件加速的核心功能之一 . 使用CSS进行转换效率明显提高 .
从您的问题中不清楚您使用此技术尝试实现的目标,但这种方法可能会有所帮助 . 我用它来创建用于矩阵变换的补间数组 .
为了实现这一点,只需为开始和结束位置创建元素,并为要跟随补间路径的元素创建元素 .
使用css将任何
transform
规则添加到#start和#end . 如果要隐藏它们,请设置css规则display:none
.然后,您可以使用以下内容显示结果:
并使用以下命令调用animate函数:
别忘了在div上设置
perspective-origin
,在父元素上设置perspective
.https://jsfiddle.net/tnt1/wjunsj36/2/
希望这可以帮助 )
示例中的旋转矩阵具有域[-180°,180°] . 高于180°的度数不在函数域之内 .
您可以通过以下方式将旋转角度映射到正确的域:
在评估rotation-function中的matrix-elements之前,必须调用此函数:
注意:我不是java脚本程序员 . 我希望语法是正确的 .