Martin

爱设计,爱创造|To design and create

谈谈前端的 2D 矩阵变换

前言

最近在搞一个图形编辑类的前端项目,其中底层涉及到大量矩阵变换,于是总结心得遂成此文。

先引出矩阵

2D 变换矩阵主要形式如下:

[acebdf001]\begin{bmatrix} a&c&e\\ b&d&f\\ 0&0&1 \end{bmatrix}

下面我们来对此矩阵进行分解。

分解矩阵

变换基量

举个例子。

下图是两个用两维的笛卡尔坐标系表示的二维平面:黑色坐标系 x-y、蓝色坐标系 i-j

image

已知点 p 处在 i-j 坐标系中,坐标为 (2, 1),又知 i-j 坐标系在 x-y 坐标系的基量为 i =(1, 1)j=(-1, 1)。那么点 p 在坐标系 x-y 的坐标是什么?

解:p = 2i+j = 2(x+y)+(-x+y) = x+3y,因此点 p 在 x-y 坐标系中的坐标为 (1, 3)

所以一般地,假设点 p 在 i-j 坐标系下为 (k1, k2),在 x-y 坐标系下为 (q1, q2)。同样地有基向量 i 对应在 x-y 空间中为 (m1, m2)j 对应在 x-y 空间中为 (n1, n2),则有:

p=k1(m1x+m2y)+k2(n1x+n2y)\vec{p} = k_{1}(m_{1}\vec{x} + m_{2}\vec{y}) + k_{2}(n_{1}\vec{x} + n_{2}\vec{y})

最后得到:

q1=k1m1+k2n1q2=k1m2+k2n2q_{1} = k_{1}m_{1} + k_{2}n_{1}\\ q_{2} = k_{1}m_{2} + k_{2}n_{2}

即:

(q1 q2)=(k1 k2) [m1m2n1n2]\left(q_{1}\ q_{2}\right) = \left(k_{1}\ k_{2}\right)\ \begin{bmatrix}m_{1}&m_{2}\\n_{1}&n_{2}\end{bmatrix}

这里的矩阵对应文章一开头矩阵中的 abcd

偏移

得到 x-y 坐标下的点 p 后,我们允许点 p 能在该坐标系下偏移,产生 x 方向与 y 方向对应的偏移量 ef,则有:

q1=q1+eq2=q2+fq_{1}' = q_{1} + e\\ q_{2}' = q_{2} + f

CSS3 中的常见矩阵

其实,transform: matrix(a,b,c,d,e,f),就是对应文章开头的矩阵,结合上面得到的分解,可以得到对应公式:

[acebdf001][xy1]=[ax+cy+ebx+dy+f0+0+1]\begin{bmatrix}a&c&e\\b&d&f\\0&0&1\end{bmatrix}\cdot\begin{bmatrix}x\\y\\1\end{bmatrix}=\begin{bmatrix}ax + cy +e\\bx + dy + f\\0 + 0 + 1\end{bmatrix}

根据此公式,我们可以推导出 css 中内置 transform 函数中的变换矩阵。

translate

1
transform: translate(tx, ty);

等同于:

1
transform: matrix(1, 0, 0, 1, tx, ty);

scale

1
transform: scale(sx, sy);

等同于:

1
transform: matrix(sx, 0, 0, sy, 0, 0);

rotate

1
transform: rotate(θx deg, θy deg);

等同于:

1
transform: matrix(cosθ, sinθ, -sinθ, cosθ, 0, 0);

skew

1
transform: skew(θx deg, θy deg);

等同于:

1
transform: matrix(1, tan(θy), tan(θx), 1, 0, 0);

高级矩阵变换

变换叠加

见于 transform 有多个变换或者父子节点嵌套 transform,其实就是将这些变换矩阵进行相乘运算:

[acebdf001][acebdf001]=[aa+cbac+cdae+cf+eba+dbbc+ddbe+df+f001]\begin{bmatrix}a&c&e\\b&d&f\\0&0&1\end{bmatrix}\cdot\begin{bmatrix}a'&c'&e'\\b'&d'&f'\\0&0&1\end{bmatrix}=\begin{bmatrix}aa'+cb'&ac'+cd'&ae'+cf'+e\\ba'+db'&bc'+dd'&be'+df'+f\\0&0&1\end{bmatrix}

反变换

这种变换并不常见,其实是逆矩阵运算:

[acebdf001]1=[dadbccbcaddecfbcadbbcadaadbcbeafadbc001]\begin{bmatrix}a&c&e\\b&d&f\\0&0&1\end{bmatrix}^{-1} = \begin{bmatrix}\dfrac{d}{ad-bc}&\dfrac{c}{bc-ad}&\dfrac{de-cf}{bc-ad}\\\dfrac{b}{bc-ad}&\dfrac{a}{ad-bc}&\dfrac{be-af}{ad-bc}\\0&0&1\end{bmatrix}

总结

矩阵变换的见解(选自孟岩的 blog)

  1. 线性空间中的任何一个对象,通过选取基和坐标的办法,都可以表达为向量的形式。这里的基可以看成是坐标系。
  2. 只要我们选定一组基,那么对于任何一个线性变换,都能够用一个确定的矩阵来加以描述。
  3. 矩阵不仅可以作为线性变换的描述,而且可以作为一组基的描述。而作为变换的矩阵,不但可以把线性空间中的一个点给变换到另一个点去,而且也能够把线性空间中的一个坐标系(基)表换到另一个坐标系(基)去。

回到 css

  1. 任何内置变换函数都可以用一种变换矩阵来表示
  2. 须注意变换原点的位置:transform-origin

Ref

  1. 从坐标系图中理解“空间变换”
  2. 理解CSS3 transform中的Matrix(矩阵)

Proudly powered by Hexo and Theme by Hacker
© 2021 Martin Yong