理论上而言,一个点是不存在什么缩放变换的,但考虑到所有图像都是由点组成,因此,如果图像在 x 轴和 y 轴方向分别放大 k1 和 k2 倍的话,那么图像中的所有点的 x 坐标和 y 坐标均会分别放大 k1 和 k2 倍。
用公式表示就是: (x y) 代表原坐标的点, (x',y') 代表新坐标的点。
如果用矩阵来表示就是:
我们分别进行两个测试, X 方向测试矩阵如下:
ctx.fillStyle = '#f36'; ctx.transform(.5, 0, 0, 1, 0, 0); ctx.fillRect(0,0,100,100);
效果如下:
可以看到,正方形在 X 方向上进行了缩放,变成了之前的 0.5 倍。再进行 Y 方向的矩阵测试:
ctx.fillStyle = '#f36'; ctx.transform(1, 0, 0, .5, 0, 0); ctx.fillRect(0,0,100,100);
效果如下:
没有问题, Y 方向变成原来的 0.5 倍。
注:从上面的示例可以看出来,不管是 X 轴还是 Y 的缩放都是基于原点进行的。如果需要基于元素自身中心点做缩放,那需要在 transform() 或 setTransform() 之前先做 translate() 操作。当然也可以使用 transform() 或 setTransform() 来替代 translate() 。
旋转前面我们看到了怎么通过 transform() 和 setTransform() 方法来实现 translate() 和 scale() 的效果,接下来看怎么实现 rotate() 效果。因为旋转涉及到角度的问题,这里有一个数学知识很有必要先进行了解。
假定有一个点 P(x0,y0) 相对坐标原点顺时针旋转 θ 到达点 P(x,y) ,同时假定 P 点离坐标原点的距离为 r ,如下图所示:
那么要计算出 P 点的坐标,就需要运用到两角和公式:
sin(α + θ) = sin(α)*cos(θ) + cos(α)*sin(θ) cos(α + θ) = cos(α)*cos(θ) - sin(α)*sin(θ)除了有两角和公式之外,还有对应的两角差公式:
sin(α - θ) = sin(α)*cos(θ) - cos(α)*sin(θ) cos(α - θ) = cos(α)*cos(θ) + sin(α)*sin(θ)根据上图我们可以推导出, P0 点以半径 r 旋转一定的角度 α ,然后位置在 (x0,y0) 位置处,并且从 (x0,y0) 处继续围绕原点旋转 θ 度到达 P 点 (x,y) 处。根据前面的两角和公式,可以计算出 P 点 (x,y) 的值:
x = r * cos(α + θ) = r*cos(α)*cos(θ) - r*sin(α)*sin(θ) y = r * sin(α + θ) = r*sin(α)*cos(θ) + r*cos(α)*sin(θ)而 x0 = r*cos(α); y0=r*sin(θ) ,那么:
x = r * cos(α + θ) = r*cos(α)*cos(θ) - r*sin(α)*sin(θ) = x0*cos(θ) - y0*cos(θ) y = r * sin(α + θ) = r*sin(α)*cos(θ) + r*cos(α)*sin(θ) = y0*cos(θ) + x0*sin(θ)如果用矩阵,就可以表示为:
我们来进行一个 90 度旋转的测试, cos(90)=0,sin(90)=1 ,所以矩阵应该写成:
将上面的矩阵通过 transform() 来表示就是 transform(0,1,-1,0,0,0) 。来看在Canvas中的效果:
ctx.fillStyle = '#f36'; ctx.transform(0,1,-1,0,0,0); ctx.fillRect(0,0,200,100);如果就这样的话,大家在Canvas的画布中看不到任何的图形,有可能还以为是出错了,其实并非如此。为什么呢?因为如果从 (0,0) 点绘制一个旋转的图,那么肯定已经转到屏幕外面了,所以我们给它加一个偏移试试(位移前面介绍过了):
ctx.fillStyle = '#f36'; ctx.transform(0,1,-1,0,200,50); ctx.fillRect(0,0,200,100);这下效果就出来了:
斜切
斜切变换Skew在数学上又称为Shear Mapping或者Transvection,它是一种比较特殊的线性变换。大家不知道是否还记得,在上一节或者说本节内容前面,我们提到Canvas自定的变换有 rotate() 、 scale() 和 translate() ,就是没有看到 skew() 这样的方法。虽然没有自带 skew() 这样的方法,但值得庆幸的是,可以通过 transform() 或者说 setTransform() 方法来实现。接下来我们来看看怎么实现斜切变换。
斜切变换的效果就是让所有点的 x 坐标(或者 y 坐标)保持不变,而对应的 y 坐标(或者 x 坐标)按比例发生平移,且平移的大小和该点到 x 轴(或 y 轴)的垂直距离成正比。斜切变换,属于面积变换,即一个形状在斜切变换的前后,其面积是相等的。
比如下图,各点的 y 坐标保持不变,但其 x 坐标则按比例发生了平移。这种情况将发生水平斜切:
下图各点的 x 坐标保持不变,但其 y 坐标则按比例发生了平移。这种情况将发生垂直斜切:
与旋转变换相比,旋转变换是旋转坐标系中的点,而斜切内里是转坐标轴,坐标轴旋转之后,要保持各点的坐标值不变,所有各点的位置就变了,如下图:
变换后,点在新坐标系中的位置不变,在原坐标系中的位置是:
f(x) = x * tan(α) f(y) = y * tan(α)计算如下:
回到Canvas当中来,假定有一个点 P0(x0,y0) 经过斜切变换后得到 P(x,y) ,对于水平斜切,它们之关的关系是:
x = x0 + k*y0 y = y0用矩阵表示就是:
扩展到 3 x 3 的矩阵就是下面这样的形式:
同理,对于垂直斜切,可以有:
在数学上严格的斜切变换就是上面这样,但在Canvas中除了有上面说的情况之外,还可以同时进行水平、垂直斜切,那么矩阵就变成: