想要了解齐次坐标,先要了解矩阵中的平移,缩放和旋转。
1. 学习知识点
- 什么是仿射变换,什么叫线性变换?
- 平移,旋转与缩放。
- 齐次坐标
- 案例:transform matrix 理解
2. 什么是仿射变换,什么叫线性变换?
仿射变换简单来说就是“线性变换 + 平移”。
实际上在平常的 Web 开发中,我们也经常会用到仿射变换,比如,对元素设置 CSS 的 transform 属性就是对元素应用仿射变换。 线性变换主要是指,旋转和缩放
2.1 仿射变换的2个性质:
- 仿射变换前是直线段的,仿射变换后依然是直线段。
- 对两条直线段 a 和 b 应用同样的仿射变换,变换前后线段长度比例保持不变。
由于仿射变换具有这两个性质,因此对线性空间中的几何图形进行仿射变换,就相当于对它的每个顶点向量进行仿射变换。
2.2 线性变换的两个性质:
- 线性变换不改变坐标原点(因为如果 x0、y0等于零,那么 x、y 肯定等于 0)。
- 线性变换可以叠加,多个线性变换的叠加结果就是将线性变换的矩阵依次相乘,再与原始向量相乘。
3. 平移,旋转与缩放
常见的仿射变换主要包括,平移,旋转,缩放以及他们的组合。
3.1 平移的几何意义
如果我们想让向量 P(x, y) 沿着向量 Q(x1, y1) 平移,只要将 P 和 Q 相加就可以了。
3.2 旋转的几何意义
如果我们想让向量 P(x, y) 沿着向量 Q(x1, y1) 平移,只要将 P 和 Q 相加就可以了。
因为 rcos(a),rsin(a)是向量p原始的坐标x0,y0,所以,带入坐标公式中:
最终我们代数矩阵形式中就得到了下面的公式:
3.2 缩放的几个意义
缩放变换也很简单,我们可以直接让向量与标量(标量只有大小、没有方向)相乘。
矩阵的表达形式:
3.3 推导线性变换公式
根据线性变换性质2:线性变换可以叠加,多个线性变换的叠加结果就是将线性变换的矩阵依次相乘,再与原始向量相乘。 可得出:
3.4 仿射变换的终极公式
仿射变换的终极公式是:
4. 齐次坐标
由于仿射变换中的即存在矩阵的乘法,又存在矩阵的加法,导致在矩阵的运算中十分不方便,因此,我们需要把它升高维度,让它变成线性变换。
而这种将原本 n 维的坐标转换为了 n+1 维的坐标。这种 n+1 维坐标被称为齐次坐标,对应的矩阵就被称为齐次矩阵。
齐次坐标和齐次矩阵是可视化中非常常用的数学工具,它能让我们用线性变换来表示仿射变换。 这样一来,我们就能利用线性变换的叠加性质,来非常方便地进行各种复杂的仿射变换了。
4.1 矩阵叉乘的运算方式
5. 案例:transform matrix 理解
4.1 实现案例:
对于div实现先旋转 30 度,然后平移 100px、50px,最后再放大 0.5 倍。实际上相当于我们做了如下变换;
4.2 普通api的写法
div.block{
rotate(30deg) translate(100px,50px) scale(1.5)
}
4.2 矩阵写法
首先根据矩阵乘法,我们要实现一个矩阵乘法的函数
function multiply(out, a, b) {
let a00 = a[0],
a01 = a[1],
a02 = a[2];
let a10 = a[3],
a11 = a[4],
a12 = a[5];
let a20 = a[6],
a21 = a[7],
a22 = a[8];
let b00 = b[0],
b01 = b[1],
b02 = b[2];
let b10 = b[3],
b11 = b[4],
b12 = b[5];
let b20 = b[6],
b21 = b[7],
b22 = b[8];
out[0] = b00 * a00 + b01 * a10 + b02 * a20;
out[1] = b00 * a01 + b01 * a11 + b02 * a21;
out[2] = b00 * a02 + b01 * a12 + b02 * a22;
out[3] = b10 * a00 + b11 * a10 + b12 * a20;
out[4] = b10 * a01 + b11 * a11 + b12 * a21;
out[5] = b10 * a02 + b11 * a12 + b12 * a22;
out[6] = b20 * a00 + b21 * a10 + b22 * a20;
out[7] = b20 * a01 + b21 * a11 + b22 * a21;
out[8] = b20 * a02 + b21 * a12 + b22 * a22;
return out;
}
矩阵的乘法本质上,就是每一行乘以每一列;
构建各种的矩阵
const rotate = [
Math.cos(rad), -Math.sin(rad), 0,
Math.sin(rad), Math.cos(rad), 0,
0, 0, 1,
];
const translate = [
1, 0, 100,
0, 1, 50,
0, 0, 1
];
const scale = [
0.5, 0, 0,
0, 0.5, 0,
0, 0, 1
];
各种类型的矩阵相乘
var out = [rotate, translate, scale].reduce((a, b) => multiply([], b, a));
// 结果:
[
0.43301270189221935, -0.24999999999999997, 61.60254037844388,
0.24999999999999997, 0.43301270189221935, 93.30127018922192,
0, 0, 1
]
根据矩阵api填充参数
transform: matrix(a,b,c,d,e,f);
其对应关系是: 因此最终使用的结果:
dom.style.transform = `matrix(${[
out[0],
out[3],
out[1],
out[4],
out[2],
out[5],
].join(",")})`;
// 这个效果与上面的效果是一样的
rotate(30deg) translate(100px,50px) scale(1.5)
致谢
如果感觉我的文章有用,请关注下我的公众号: 前端小黄。 我将不定期更新我的原创文章。 本文章源码地址:github.com/hpstream/We…;