Canvas 变形 transform()
变形 ( Transforms ) 就是将平移 (translate),缩放(scale),旋转 (rotate) 等几种几何变换合为一体,使用矩阵来操作多种几何变换
但是,Canvas 中的变形 ( Transforms ) 针对的不是绘制的图形,而是针对画布本身
画布变形最重要的就是理解矩阵,醉了,又是一个烧脑的数学游戏
如果你对矩阵不了解,可以看看网易的公开课 可汗学院::可汗学院公开课::线性代数::矩阵简介
矩阵
当然,矩阵,很复杂,很深奥,所以,你还是看我的解释吧
笛卡尔坐标系面积
大家知道怎么计算笛卡尔坐标系上某个点 (x,y) 与原点 (0,0) 组成的矩形的面积吗?
我们使用屏幕坐标系,而不是真正的笛卡尔坐标系,它们之间的不同点是笛卡尔坐标系的 Y 轴正方向是向上的
例如下图中的点 (100,50)
很简单对吧,就是 x * y = 100 x 50 = 5000
如果我们将 (x,y) 分别平移 (dx,dy) ,例如 (10,20),又要怎么计算面积呢
也很简单对吧,就是
(x+dx) * (y+dy) = (100+10) x (50+20) = 110 x 70 = 7700
当然,还有一种算法,就是分别求 4 个小块的面积,再求和
(x * y) + (x * dy) + (y * dx ) + (dx * dy ) = (100 * 50) + (100 * 20) + (50 * 10 ) + (20 * 10) = 5000 + 2000 + 500 + 200 = 7700
好了,也就是说
(x+dx) * (y+dy) == (x * y) + (x * dy) + (y * dx ) + (dx * dy )
要怎么得到这个公式呢?
很简单的,把图中所有的黑色和紫色虚线换成逗号,再给它们加上中括号变成数组,就会成为
大家可以看到,右边的数字都是乘号算法( * ),所以左边两个也应该是相乘才对,然后再把箭头换成等号(=),就是下面这样了
然后我们再把逗号换成空格,像这样
这就是矩阵乘法啦,左边的两个都是矩阵,相乘之后得到一个新的矩阵
如果我们再把 (x,y) 右移 a 的距离,我们假设 a=20
,就会变成这样了
面接求解也就很简单了对吧
(x + dx + a) * (y + dy) = (100+10+20) x (50+20) = 130 x 70 = 9100
我们同样用求 6 小块面积的方法来求解,就变成了
(x * y) + (x * dy) + (y * dx ) + (dx * dy ) + (a * y) + (a * dy) = (100 * 50) + (100 * 20) + (50 * 10 ) + (20 * 10) + (20*50) + (20*20) = 5000 + 2000 + 500 + 200 + 1000 + 400 = 9100
然后我们按照上面的方式如法炮制,就得到一个矩阵
从上面的演示中可以看出
新矩阵的元素数量为第一个矩阵的行数 (m ) 乘以第二个矩阵的列数 ( n )
例如上图等于 2 x 2 = 4
然后我们也能看到,矩阵的乘法是这样的
结果矩阵第 m 行与第 n 列交叉位置的那个值,等于第一个矩阵第m行与第二个矩阵第n列,对应位置的每个值的乘积之和
废话不多说,我们直接看一个范例
矩阵与变形的关系
那么矩阵与变形有什么关系呢?
假设我们的几何变换: 平移 (translate),缩放(scale),旋转 (rotate) 和 斜拉(skew)
需要的参数如下
参数 | 说明 |
---|---|
a | 水平缩放 |
b | 水平倾斜 |
c | 垂直倾斜 |
d | 垂直缩放 |
e | 水平移动 |
f | 垂直移动 |
我们把它们放在一个矩阵里,就像下面这样
注意书写方向是竖的
然后我们再把它和一个坐标(x,y) 相乘,因为第二个矩阵的列数要和第一个矩阵的行数相同,所以我们要把坐标再加上一个元素 1
根据上面的矩阵乘法,就会得到右边的那个矩阵
这个矩阵有什么用呢?
它用于将一个坐标应用多种几何变换,把几何变换之后新的坐标点的计算方法简化了
是的,没错
矩阵的唯一作用就是简化多种几何变换之后新的坐标的计算方式
如果你看不懂前面这些都没关系,直接记住参数就好了
Canvas transform()
ctx.transform()
可以一次给 Canvas 添加多种变换,我们可以使用它缩放、旋转、移动和倾斜画布
语法
void ctx.transform(a, b, c, d, e, f);
参数 | 说明 |
---|---|
a | 水平缩放 |
b | 水平倾斜 |
c | 垂直倾斜 |
d | 垂直缩放 |
e | 水平移动 |
f | 垂直移动 |
如果任意一个参数是无限大,变形矩阵也必须被标记为无限大,否则会抛出异常
范例
下面的范例先在点 (50,50) 绘制一个橘黄色的 100x50
的矩形,然后将画布变形 transform(1,1,0,1,0,0)
最后在点 (50,50) 绘制一个 100x50
的绿色矩形
<!DOCTYPE html> <meta charset="utf-8"> <canvas id="canvas-1" width="400" height="300"> </canvas> <script> var c = document.getElementById("canvas-1"); var ctx = c.getContext("2d"); ctx.fillStyle="orange"; ctx.fillRect(50,50,100,50); ctx.transform(1,1,0,1,0,0); ctx.fillStyle="green"; ctx.fillRect(50,50,100,50); </script>
运行结果如下
范例 2
我们画一个好玩的
<!DOCTYPE html> <meta charset="utf-8"> <canvas id="canvas-2" width="400" height="300"> </canvas> <script> var c = document.getElementById("canvas-2"); var ctx = c.getContext("2d"); var sin = Math.sin(Math.PI/6); var cos = Math.cos(Math.PI/6); ctx.translate(100, 100); var cc = 0; for (var i=0; i <= 12; i++) { cc = Math.floor(255 / 12 * i); ctx.fillStyle = "rgb(255," + cc + "," + cc + ")"; ctx.fillRect(0, 0, 100, 10); ctx.transform(cos, sin, -sin, cos, 0, 0); } </script>
运行结果如下