theme: qklhk-chocolate
  1. 为什么硬件加速要使用transform,以及为什么硬件加速会快?

小科普:关于矩阵的乘法

	以两个二阶齐次矩阵相乘为例
	[                 [                      [
	 a11,a12,     *     b11,b12,       =      a11*b11 + a12* b21 , a11*b12 + a12*b22,
	 a21,a22            b21,b22               a21*b11 + a22* b21 , a21*b12 + a22*b22
	]                  ]                      ]

由此,可以看到两个矩阵相乘就是拿第一个的每一行,乘以第二个的每一列,因此两个矩阵相乘也有个规定就是第一个矩阵的列数(每一行元素的个数),要与第二个矩阵的行数(每一列元素的个数)相等才可以发生乘法运算。

其中a1 b1 c1 d1 a2 b2 c2 d2 a3 b3 c3 d3,代表了线性变换,a4 b4 c4 d4代表的是位移变换。若空间中点的表示是一个列向量表示,那么,他的矩阵形式应该是这样的:

  a1 a2 a3 a4
  b1 b2 b3 b4
  c1 c2 c3 c4
  d1 d2 d3 d4

在下边的例子中我们都假定空间中的点是以列向量形式表示的。使用右手坐标系。

(实际这里也可以写成该矩阵的转置形式,在下边进行乘法运算时都分别转置下,然后交换两个矩阵的位置也是可以的,结果是一样的)

3d变换的初始矩阵如下:

1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1

translate

translate代表一个位移变换,3d的translate变换矩阵对应的是一个如下的4阶齐次矩阵 T :

 1 0 0 dx
 0 1 0 dy
 0 0 1 dz
 0 0 0 1

假设空间内某个点D的坐标形如:

 x
 y
 z
 1

给他施加一个translate矩阵之后就可以得到的结果为 T*D :

 1*x + 0*y + 0 * z + dx,               x + dx
 0*x + 1*y + 0 * z + dy,         =     y + dy
 0*x + 0*y + 1 * z + dz,               z + dz
 0 +   0   +  0    + 1,                   1

以上的变换写成matrix3d函数的形式就是:
matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, dx, dy, dz, 1)
等价于:
translate3d(x,y,z)

scale

scale表示的是一个缩放变换,缩放的具体数值体现在主对角线上,比如一个1.5倍的缩放矩阵S:

1.5   0   0   0
 0   1.5  0   0
 0    0  1.5  0
 0    0   0   1

给上边的点D施加一个缩放矩阵得到的结果是 S*D:

	1.5*x + 0*y   + 0*z  + 0 * 1      1.5x
    0*x  + 1.5*y + 0*z  + 0 * 1   =  1.5y
    0*x +  0*y +  1.5*z + 0 * 1      1.5z
    0*x +  0*y +   0*z  + 1 * 1       1

等价于:
matrix3d(1.5, 0, 0, 0, 0, 1.5, 0, 0, 0, 0, 1.5, 0, 0, 0, 0, 1)
可简写为:
scale3d(1.5, 1.5, 1.5)

rotate

rotate 代表旋转

二维平面的rotate

二维平面的rotate代表某个点绕着某个点旋转,一个二维变换可以使用一个三阶齐次矩阵表示,其矩阵的推导可以这么推导:

  1. 首先是主对角线上的缩放数值,和第三列的位移值,都为默认值1(代表原始大小)和0(代表原始位置):
1 0 0
0 1 0
0 0 1
  1. 我们拿如下二维坐标系举例:
    矩阵的乘法运算与css的3d变换(transform)-LMLPHP

将两个单位向量分别在x y轴上旋转θ角度,得到的A'便是x轴上旋转矩阵的值,得到的B' 便是y轴上旋转矩阵的值,因此2d的旋转矩阵是这样的(图片上的坐标系是以行向量表示的,为了统一 我们进行一次转置):

cos0   -sin0
sin0   cos0

3d空间的rotate

在一个3d空间中,旋转不再是绕某个点的旋转,而是绕某个轴的旋转(x,y,z轴),以绕x轴的旋转举例(这个图有点出入,实际BB'应该与y轴是平行的,AA'与Z轴平行,理解就好,没办法绘画功底实在太差 - -,绕某个轴旋转就是想象该条轴不动然后另一条与他垂直的轴绕着其旋转):
矩阵的乘法运算与css的3d变换(transform)-LMLPHP

因此绕x轴的旋转矩阵可以写成这样:

1    0    0     0
0  cos0  -sin0  0
0  sin0   cos0  0
0    0     0    1

我们可以写一个rotateX(60deg),那么对应的matrix3d的参数就是:
matrix3d(1, 0, 0, 0, 0, 0.5, 0.866025, 0, 0, -0.866025, 0.5, 0, 0, 0, 0, 1)
同理可得出绕z轴和y轴的旋转矩阵:

Z:
cos0 -sin0 0 0
sin0 cos0  0 0
 0    0    1 0
 0    0    0 1
 
 Y:
 cos0   0   sin0   0
  0     1     0    0
 -sin0  0   cos0   0
  0     0     0    1

至此,css中的3d变换大体上讲完了,然后将以上几种矩阵经过不同组合相乘就能得到复合变换,值得注意的是 矩阵乘法一般不满足交换律,所以运算顺序还是比较重要的。

那为什么图形变换在计算机中一般使用矩阵表示呢?据我现在看到的资料来看,就是方便,使用矩阵运算之后,可以将多种变换统一成矩阵的乘法运算,方便计算机流水线式处理。
最后回答一下第二个问题:

最后分享一个矩阵的乘法运算,可以验证一下css transform相关的矩阵运算

 // 矩阵相乘
 function multipy(a,b){
        let r = a.length;
        let col = a[0].length;
        let result = [];
        for( let i = 0; i < r; i++ ){

            let row = a[i];
            result[i] = [];

            for( let j = 0; j < r; j++){
                let count = 0;
                for( let x = 0;x < col; x++ ){
                    let item1 = row[x];
                    let item2 = b[x][j]
                    count += (item1*item2)
                }
                result[i].push(count)


            }
        }
        return result;

    }
    const deg45 = Math.PI/4;
10-05 16:15