从matrix变换函数矩阵来说明CSS Transform的原理和操作

文章以二维变换为例说明,三维变换采用类似的操作理解即可。

变换的座标系统

默认的变换座标系统,变换原点为元素中心,x轴与y轴与web中定义的一致。其中,变换原点可以用transform-origin属性来设置。transform是以该座标系统为参考,对变换中图形上每个点的座标进行更新,来得到变换后的图形。

线性变换

线性变换需要满足两个条件:

  1. 不改变座标原点
  2. 每个点的座标在更新时,采用线性函数来更新。用数学中的线性方程表达如下{x=ax+cyy=bx+dy\begin{cases}x\prime =&ax+cy\\ y\prime =&bx+dy\end{cases}
    用矩阵的形式表示为[xy]=[acbd][xy]\begin{bmatrix}x\prime \\ y\prime \end{bmatrix} =\begin{bmatrix}a&c\\ b&d\end{bmatrix} \begin{bmatrix}x\\ y\end{bmatrix}
    CSS Transform中,除了translate变换外,其他都属于线性变换。因此,除了translate外,其他变换可以用2*2的矩阵描述如下[acbd]\begin{bmatrix}a&c\\ b&d\end{bmatrix} ,而translate变换需要额外描述,因此CSS Transform的矩阵描述如下[acebdf001]\begin{bmatrix}a&c&e\\ b&d&f\\ 0&0&1\end{bmatrix} 其中,[acebdf]\begin{bmatrix}a&c&e\\ b&d&f\end{bmatrix} 其实足够描述二维变换了,最后一行是为了矩阵运算而补充的。e和f就是为了translate而增加的额外描述,e用来描述translateX,f用来描述translateY。

matrix()函数

CSS Transform中的所有变换,实际上都是matrix()函数的封装,为了便于记忆和使用。在二维变换中,matrix()函数接收6个参数为matrix(a, b, c, d, e, f),即为上文中的矩阵元素。matrix()函数本质上就是上文中的变换矩阵,对于不同的变换,6个参数各不相同。
下列变换中,假设没有变换时,图形上点的座标为(x, y),变换后为(x’, y’)。为了方便运算,点的座标额外增加一行为1;

没有变换

matrix矩阵为[100010001]\begin{bmatrix}1&0&0\\ 0&1&0\\ 0&0&1\end{bmatrix} ,为一个3*3的单位矩阵。因此,所有点的座标没有任何变化。

translate变换

translate变换只是给e和f赋值,其中translateX()给e赋值,translateY()给f赋值,此时matrix矩阵为[10e01f001]\begin{bmatrix}1&0&e\\ 0&1&f\\ 0&0&1\end{bmatrix}
变换的过程表示为[xy1]=[10e01f001][xy1]\begin{bmatrix}x\prime \\ y\prime \\ 1\end{bmatrix} =\begin{bmatrix}1&0&e\\ 0&1&f\\ 0&0&1\end{bmatrix} \begin{bmatrix}x\\ y\\ 1\end{bmatrix} 线性方程为
{x=x+ey=y+f\begin{cases}x\prime =&x+e\\ y\prime =&y+f\end{cases}
可以看出,translate变换相当于在原来座标系统下,每个点的x座标增加e,y座标增加f,得到了新的座标。

改变座标原点的理解:也可以这样理解,让e和f都等于0,此时每个点的座标都不变,而座标系统的原点从(0, 0)移动到了(e, f)。从这个角度来看,translate改变了座标原点的位置。也就是说,原来的图形座标都不用变,只要把座标原点移动到(e, f)的位置即可。注意:(e, f)这个位置座标是以原座标系统为参考点的。用这种方式理解的前提是,原来的座标系统用来定位新的座标系统,新的座标系统用来定位图形的座标。

rotate变换

rotate()变换是指围绕原点,旋转某个角度α,正数表示顺时针旋转,负数表示逆时针旋转。对应于matrix矩阵,指的是[cosαsinα0sinαcosα0001]\begin{bmatrix}cos\alpha &-sin\alpha &0\\ sin\alpha &cos\alpha &0\\ 0&0&1\end{bmatrix} 变换的过程表示为[xy1]=[cosαsinα0sinαcosα0001][xy1]\begin{bmatrix}x\prime \\ y\prime \\ 1\end{bmatrix} =\begin{bmatrix}cos\alpha &-sin\alpha &0\\ sin\alpha &cos\alpha &0\\ 0&0&1\end{bmatrix} \begin{bmatrix}x\\ y\\ 1\end{bmatrix} 线性方程为{x=xcosαysinαy=xsinα+ycosα\begin{cases}x\prime =&x\cos \alpha -y\sin \alpha \\ y\prime =&x\sin \alpha +y\cos \alpha \end{cases}
可以看出,旋转变换是对x和y座标进行三角函数运算

scale变换

scale()变换是指对图形的缩放,按照某个比例放大或缩小图形。缩放是对x轴和y轴座标刻度的更改,因此对应于matrix矩阵,指的是[a000d0001]\begin{bmatrix}a&0&0\\ 0&d&0\\ 0&0&1\end{bmatrix} a表示x轴的缩放比例,d表示y轴的缩放比例。变换的过程为[xy1]=[a000d0001][xy1]\begin{bmatrix}x\prime \\ y\prime \\ 1\end{bmatrix} =\begin{bmatrix}a&0&0\\ 0&d&0\\ 0&0&1\end{bmatrix} \begin{bmatrix}x\\ y\\ 1\end{bmatrix} 线性方程为{x=axy=dy\begin{cases}x\prime =&ax\\ y\prime =&dy\end{cases} 可以看出,的确是将x座标乘以a,将y座标乘以d。

skew变换

skew()变换指的是将图形的水平线或垂直线做一定程度的倾斜,即看起来变歪了。由于倾斜涉及到角度,因此matrix矩阵为[1tanθx0tanθy10001]\begin{bmatrix}1&\tan \theta_{x} &0\\ \tan \theta_{y} &1&0\\ 0&0&1\end{bmatrix} 其中,θx和θy分别为skewX()和skewY()的参数,θx表示垂直线倾斜角(正数表示向x轴正半轴倾斜),θy表示水平线倾斜角(正数表示向y轴正半轴倾斜)。这一点从线性方程中可以看出。变换过程为[xy1]=[1tanθx0tanθy10001][xy1]\begin{bmatrix}x\prime \\ y\prime \\ 1\end{bmatrix} =\begin{bmatrix}1&\tan \theta_{x} &0\\ \tan \theta_{y} &1&0\\ 0&0&1\end{bmatrix} \begin{bmatrix}x\\ y\\ 1\end{bmatrix} 线性方程为{x=x+ytanθxy=y+xtanθy\begin{cases}x\prime =&x+y\tan \theta_{x} \\ y\prime =&y+x\tan \theta_{y} \end{cases} 从中可以看出,x和y座标都是在原来基础上,增加了一部分由角度决定的量,结果就是变歪了。

复合变换

这里的复合变换,指的是一个transform属性中,包含以上几个变换,由以上几个变换按顺序组成。这里的顺序非常重要,因为顺序不同,最后得到的matrix矩阵会不同。尤其在包含translate变换的情况下,因为translate的非线性,导致的变换差异很大。
复合变换的规则是,transform属性中,按从左向右的顺序,将代表各个变换的matrix矩阵依次相乘,得到最终的matrix矩阵。由于矩阵乘法不满足交换律,因此这里的顺序很重要。另外,translate如果放在其他变换的右边,其他变换就会连translate变换一起变换。听起来很乱吧,下面用matrix矩阵相乘的法则来演示。

transform: rotate(α) translate(e, f)

以上面的变换为例,这意味着先进行translate变换,再进行rotate变换。变换过程为[xy1]=[cosαsinα0sinαcosα0001][10e01f001][xy1]\begin{bmatrix}x\prime \\ y\prime \\ 1\end{bmatrix} =\begin{bmatrix}\cos \alpha &-\sin \alpha &0\\ \sin \alpha &\cos \alpha &0\\ 0&0&1\end{bmatrix} \begin{bmatrix}1&0&e\\ 0&1&f\\ 0&0&1\end{bmatrix} \begin{bmatrix}x\\ y\\ 1\end{bmatrix} 把中间的matrix矩阵计算一下[cosαsinα0sinαcosα0001][10e01f001]=[cosαsinαecosαfsinαsinαcosαesinα+fcosα001]\begin{bmatrix}\cos \alpha &-\sin \alpha &0\\ \sin \alpha &\cos \alpha &0\\ 0&0&1\end{bmatrix} \begin{bmatrix}1&0&e\\ 0&1&f\\ 0&0&1\end{bmatrix} =\begin{bmatrix}\cos \alpha &-\sin \alpha &e\cos \alpha -f\sin \alpha \\ \sin \alpha &\cos \alpha &e\sin \alpha +f\cos \alpha \\ 0&0&1\end{bmatrix} 线性方程为{x=xcosαysinα+ecosαfsinαy=xsinα+ycosα+esinα+fcosα\begin{cases}x\prime =&x\cos \alpha -y\sin \alpha +e\cos \alpha -f\sin \alpha \\ y\prime =&x\sin \alpha +y\cos \alpha +e\sin \alpha +f\cos \alpha \end{cases}

理解方式1:座标原点不变

从这里可以看出,变换后的座标相当于对原座标做rotate变换,加上对translate增加的座标做的rotate变换。如果我们写成{x=(x+e)cosα(y+f)sinαy=(x+e)sinα+(y+f)cosα\begin{cases}x\prime =&\left( x+e\right) \cos \alpha -\left( y+f\right) \sin \alpha \\ y\prime =&\left( x+e\right) \sin \alpha +\left( y+f\right) \cos \alpha \end{cases} 可以看出,先做translate变换,然后将变换后的座标整体再做rotate变换。我们的座标系统没有变,座标原点仍然是(0, 0),将translate所做变换后的座标,再一起做rotate变换。对应到图形上,就是让图形先做translate变换,然后变换后的图形再做rotate变换(座标原点为(0, 0) )。

理解方式2:座标原点随translate改变

根据translate变换会更改座标原点的方式,座标原点变为[ecosαfsinαesinα+fcosα]\begin{bmatrix}e\cos \alpha -f\sin \alpha \\ e\sin \alpha +f\cos \alpha \end{bmatrix} ,此时图形的座标相对于新的座标系,则线性方程变为{x=xcosαysinαy=xsinα+ycosα\begin{cases}x\prime =&x\cos \alpha -y\sin \alpha \\ y\prime =&x\sin \alpha +y\cos \alpha \end{cases}
此时的操作相当于,对座标原点进行平移translate(e, f),然后再rotate(α),最后再对图形在新座标原点上做rotate(α)。因此,translate变换如果在其他变换的右边,则其他变换要加在translate变换上再对座标原点做变换。其实,用座标原点跟随translate改变的理解方式比较复杂。

因此,复合变换中,translate变换如果出现在其他变换的右边,那么该复合变换不是变换之间简单的线性叠加,而是translate变换会与其他变换进行线性叠加(应用到座标原点,从而得到新的座标原点),再将其他变换线性叠加(直接给图形在新座标系应用这些变换)
transform: translate(e, f) rotate(α)

作为对比,来看一下顺序相反的情况。计算得到最终的matrix矩阵为[cosαsinαesinαcosαf001]\begin{bmatrix}\cos \alpha &-\sin \alpha &e\\ \sin \alpha &\cos \alpha &f\\ 0&0&1\end{bmatrix} 线性方程为{x=xcosαysinα+ey=xsinα+ycosα+f\begin{cases}x\prime =&x\cos \alpha -y\sin \alpha +e\\ y\prime =&x\sin \alpha +y\cos \alpha +f\end{cases} 可以看出,先rotate操作,再直接加上translate(e, f)座标增量即可,不会出现上方的对translate()座标增量也要rotate的操作。

transform: skew(θx, θy) rotate(α)与transform: rotate(α) skew(θx, θy)的对比

由于不涉及translate变换,所以我们用2*2的矩阵表示就可以了。
transform: skew(θx, θy) rotate(α)对应的matrix矩阵为[1tanθxtanθy1][cosαsinαsinαcosα]=[cosα+sinαtanθxsinα+cosαtanθxsinα+cosαtanθycosαsinαtanθy]\begin{bmatrix}1&\tan \theta_{x} \\ \tan \theta_{y} &1\end{bmatrix} \begin{bmatrix}\cos \alpha &-\sin \alpha \\ \sin \alpha &\cos \alpha \end{bmatrix} =\begin{bmatrix}\cos \alpha +\sin \alpha \tan \theta_{x} &-\sin \alpha +\cos \alpha \tan \theta_{x} \\ \sin \alpha +\cos \alpha \tan \theta_{y} &\cos \alpha -\sin \alpha \tan \theta_{y} \end{bmatrix}
线性方程为{x=(xcosαysinα)+(xsinα+ycosα)tanθxy=(xsinα+ycosα)+(xcosαysinα)tanθy\begin{cases}x\prime =&\left( x\cos \alpha -y\sin \alpha \right) +\left( x\sin \alpha +y\cos \alpha \right) \tan \theta_{x} \\ y\prime =&\left( x\sin \alpha +y\cos \alpha \right) +\left( x\cos \alpha -y\sin \alpha \right) \tan \theta_{y} \end{cases} 可以发现,正好是rotate后的座标再进行skew()的过程。如果我们定义xr和yr为rotate后的座标,可以清晰地发现这一点。rotate后的座标如下{xr=xcosαysinαyr=xsinα+ycosα\begin{cases}x_{r}=&x\cos \alpha -y\sin \alpha \\ y_{r}=&x\sin \alpha +y\cos \alpha \end{cases} 再进行skew的座标如下{x=xr+yrtanθyy=yr+xrtanθx\begin{cases}x\prime =&x_{r}+y_{r}\tan \theta_{y} \\ y\prime =&y_{r}+x_{r}\tan \theta x\end{cases}
transform: rotate(α) skew(θx, θy)对应的matrix矩阵为[cosαsinαsinαcosα][1tanθxtanθy1]=[cosαsinαtanθysinα+cosαtanθxsinα+cosαtanθycosα+sinαtanθx]\begin{bmatrix}\cos \alpha &-\sin \alpha \\ \sin \alpha &\cos \alpha \end{bmatrix} \begin{bmatrix}1&\tan \theta_{x} \\ \tan \theta_{y} &1\end{bmatrix} =\begin{bmatrix}\cos \alpha -\sin \alpha \tan \theta_{y} &-\sin \alpha +\cos \alpha \tan \theta_{x} \\ \sin \alpha +\cos \alpha \tan \theta_{y} &\cos \alpha +\sin \alpha \tan \theta_{x} \end{bmatrix} 线性方程为{x=xr+yrtanθxyr=yr+xrtanθy\begin{cases}x\prime =&x_{r}+y_{r}\tan \theta_{x} \\ y_{r}=&y_{r}+x_{r}\tan \theta y\end{cases}

这说明,线性变换之间的顺序不会引入对座标原点的变换,只要按照顺序依次应用变换即可。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章