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>

运行结果如下



参考文档

  1. 理解CSS3 transform中的Matrix

Canvas 基础教程

关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.