计算一项的结果值的方式是先计算左侧矩阵对应行和右侧矩阵对应列的第一个元素之积,然后是第二个,第三个,第四个等等,然后把所有的乘积相加,这就是结果了。现在我们就能解释为什么左侧矩阵的列数必须和右侧矩阵的行数相等了,如果不相等这一步的运算就无法完成了!
结果矩阵的维度是 (n, m) , n 等于左侧矩阵的行数, m 等于右侧矩阵的列数。
如果在脑子里想象出这一乘法有些困难,别担心。不断地动手计算,如果遇到困难再回头看这页的内容。随着时间流逝,矩阵乘法对你来说会变成很自然的事。
我们用一个更大的例子来结束对矩阵相乘的讨论。试着使用颜色来寻找规律。作为一个有用的练习,你可以试着自己解答一下这个乘法问题,再将你的结果和图中的这个进行对比(如果用笔计算,你很快就能掌握它们)。
可以看到,矩阵相乘非常繁琐而容易出错(这也是我们通常让计算机做这件事的原因),而且当矩阵变大以后很快就会出现问题。如果你仍然希望了解更多,或对矩阵的数学性质感到好奇,我强烈推荐你看看 可汗学院 的矩阵教程。
不管怎样,现在我们知道如何进行矩阵相乘了,我们可以开始学习好东西了。
矩阵与向量相乘目前为止,通过这些教程我们已经相当了解向量了。我们用向量来表示位置,表示颜色,甚至是纹理坐标。让我们更深入了解一下向量,它其实就是一个 N×1 矩阵, N 表示向量分量的个数(也叫 N 维(N-dimensional)向量)。如果你仔细思考一下就会明白。向量和矩阵一样都是一个数字序列,但它只有 1 列。那么,这个新的定义对我们有什么帮助呢?如果我们有一个M×N矩阵,我们可以用这个矩阵乘以我们的 N×1 向量,因为这个矩阵的列数等于向量的行数,所以它们就能相乘。
但是为什么我们会关心矩阵能否乘以一个向量?好吧,正巧,很多有趣的2D/3D变换都可以放在一个矩阵中,用这个矩阵乘以我们的向量将变换(Transform)这个向量。如果你仍然有些困惑,我们来看一些例子,你很快就能明白了。
单位矩阵最简单的变换矩阵就是 单位矩阵 (Identity Matrix)。单位矩阵是一个除了对角线以外都是 0 的 N×N 矩阵。在下式中可以看到,这种变换矩阵使一个向量完全不变:
向量看起来完全没变。从乘法法则来看就很容易理解来:第一个结果元素是矩阵的第一行的每个元素乘以向量的每个对应元素。因为每行的元素除了第一个都是 0 ,可得: 11+02+03+04=1 ,向量的其他 3 个元素同理。
理解Canvas中的transform、setTransform为了能更好的理解Canvas中的 transform() 和 setTransform() 方法,我们花了很大的篇幅介绍了一些基础知识。如果你理解了上述的内容,接下来的内容就能更好的理解。那么我们接下来就开始来理解这两个方法。
这两个方法都接受六个参数:
ctx.transform(a, b, c, d, e, f); ctx.setTransform(a, b, c, d, e, f);其中这六个参数可以使用一个变换矩阵来描述:
其具体代表的是:
该方法使用一个新的变化矩阵与当前变换矩阵进行乘法运算,该变换矩阵的形式如下:
你可以忽略最后一行,因为你不需要也不能修改它的值。最重要的是第一行和第二行,其中包含的数字值对应画布中使用的 a~f 。如上图所示,每一个数字值都对应一种特定的变形。
在坐标变换一切中,我们得知: 除平移 translate() 之外,旋转 rotate() 、缩放 scale() 都可以围绕一个中心点来进行,如果不指定,在默认情况下是围绕 (0,0) 原点进行相应的变换 。
而前面也说过, transform() 或 setTransform() 可以帮助我们实现 translate() 、 scale() 和 rotate() 等变形。那接下来我们来看看如何实现。
平移通过前面的知识,我们知道,不管 transform() 还是 setTransform() 都是矩阵变换。那先来看看平移对应的阵。
假定有一个点的坐标是 P(x0,y0) ,将移动到 P(x,y) 位置处,再假定在 x 轴和 y 轴方向移动的大小分别为:
如下图所示:
不难知道:
如果用矩阵来表示的话,就可以写成:
前面也介绍了矩阵怎么相乘。
理论有了,咱们来写实例。先来看 translate() 平移的效果:
ctx.fillStyle = '#f36'; ctx.translate(100, 100); ctx.fillRect(0,0,100,100);效果如下:
接下来我们换成 transform() 或 setTransform() 方法来实现:
ctx.fillStyle = '#f36'; ctx.transform(1, 0, 0, 1, 100, 100); ctx.fillRect(0,0,100,100);根据前面的矩阵,可以很轻易得到 ctx.transform(1,0,0,1,100,100) 。效果如下:
效果和前面的是一样的,也就是说: ctx.transform(1, 0, 0, 1, dx, dy); 和 ctx.translate(dx, dy) 是等效的。
注:其中 ctx.transform(1, 0, 0, 1, dx, dy) 等同于 ctx.transform(0, 1, 1, 0, dx, dy) 。
上面看到的效果是 transform() 实现的平移,其实也可以直接将 transform() 替换成 setTransform() 。
缩放