基本概念
css中transform属性值是一个或多个transform-function值。对于二维平面的变换,transform-function包括scale,rotate、translate、skew和matrix。我们目前只讨论二维变换的情形。matrix是最为基本的一个函数,其它函数其实都是可以用matrix来替代的。先看看除了matrix以外的其它四个函数。
这四个函数可以分为两类,scale,rotate、skew属于线性变换,所谓线性变换,可以理解为原点保持不变且平面上任何一条直线变换后仍然为直线的变换。只有translate不属于线性变换,因为它移动了原点。
scale、rotate和translate理解起来比较直观,无非就是缩放、旋转和平移。但是容易被忽略的是,缩放和旋转是以哪个点为原点进行的。根据经验,原点就是元素的中心点(50%, 50%),这也是默认的。但其实可以通过transform-origin设置。
对于99%的场景,知道scale、rotate和translate就够用了,甚至不需要知道transform-origin。但是,为了更深入地理解transform,我们继续往下。
skew() 是什么
先看看MDN文档的定义:
skew() 函数定义了一个元素在二维平面上的倾斜转换
看了之后一头雾水,什么叫做倾斜?倾斜和旋转有何不同?后面又有一句是这样说的:
这种转换是一种剪切映射(横切),它在水平和垂直方向上将单元内的每个点扭曲一定的角度。
这个就更抽象了,扭曲一定角度是什么意思?
先从语法上看:skew(ax, ay)
,它接收两个参数,分别表示沿横坐标和纵坐标扭曲元素的角度。也可以拆开来写,即skew(ax, ay)
等于skewX(ax)
和skewY(ay)
。想要搞懂skew,就要搞懂参数ax、ay与元素坐标变换之间的对应关系,也就是“扭曲”的过程。
先画一个长宽为100px的div,以transform-origin为原点,建立笛卡尔坐标系。前面提到过transform-origin的默认值就是元素的中心点(50%, 50%)。再画两条红线,代表坐标系的X轴和Y轴,红线交叉点就是坐标原点,如下图:
使用transform: skewX(45deg);
:
虚线代表变换前的,可以看到,skewX
实际上是保持y坐标不变,将x坐标移动一定的距离。假设某个点移动前为A,移动后后B。A向x轴作垂直线,交点为C,直线CA以C为原点逆时针旋转45度。B点就在旋转后的直线上。skewY也是相同的计算规则,只不过移动的是y坐标。
skew是在满足线性变换的前提下对坐标上的每个点进行x轴或y轴方向上的移动。如果对元素两个轴方向上偏移恰当的角度,是可以达到旋转的效果的,例如transform: skew(45deg, -45deg);
:
旋转后效果上体积变大了。但如果把skew和scale结合起来,就可以模拟rotate()方法了: transform: skew(45deg, -45deg) scale(0.75);
,效果如下:
这和transform: rotate(45deg);
的效果是一致的。
理解matrix()
matrix函数的语法是这样
matrix(a, b, c, d, tx, ty)
其中a, b, c, d分别代表函数scaleX(), skewY(), skewX(), scaleY(),它们共同代表一个线性变换。
tx和ty表示位移,分别代表translateX(), translateY() 。
例如transform: matrix(1, 0, 0, 1, 10, 10);
就等同于transform: translate(10px, 10px);
因为skew为0,scale为1,只有最后两个参数起作用。再比如transform: matrix(2,0,0,2,0,0);
等同于transform: scale(2);
。如果是实现skew或rotate的效果就会复杂一点,但是方法也是改变对应的参数。上面提到过,rotate是可以用skew和scale来实现,所以大家很容易猜到要实现rotate应该设置的a,b,c,d四个参数。matrix的六个参数涵盖了scale,skew,translate,rotate。这就是为什么matrix可以替代其它二维变换方法,并且能够通过不同的参数组合实现更多复杂的变换。这就是对matrix的感性认知。
接下来从数学的角度来分析matrix
假定我们用向量的终点来表示平面中的点,即向量(x, y)表示点(x, y)。在坐标系中确定两个基向量i(1, 0), j(0, 1)。那么平面坐标中任意一个点都可以用g*i + h*j
表示, 其中g、h为任意实数:
根据公式,只要基向量 i 和 j 确定,那么通过g和h就能确定一个点的坐标。将初始基向量设定为i(1, 0), j(0, 1)的原因是,这样g * i + h * j 等于 (g, h), 我们就可以用(g, h)来表示一个点。
如果基向量为任意值i(a, b), j(c, d),那么计算公式为
平面图形中有非常多的点,(g, h)、(k, l)、(m, n)....,如果对一个图形做线性变换,根据上面的公式,只要改变基向量 i 和 j ,那么所有点的坐标都会发生变换。而不需要去改变每个点的坐标,也就是(g, h)、(k, l)、(m, n)....都保持不变。
于是一个线性变换就可以用基向量i(a, b), j(c, d) 的坐标来表示。i , j 坐标刚好有四个数字,代表matrix的前四个参数。 再看上面设定的基向量初始坐标i(1, 0), j(0, 1),说到这,你应该知道为什么transform: matrix(1, 0, 0, 1, 0, 0);
不会产生变换效果吧,因为1,0,0,1正式代表初始变换前的基向量坐标。为了方便计算,将这四个参数写在一个2*2的矩阵里,表示二维平面上的一个线性变换。
多个线性变换依次作用,则是将矩阵依次相乘。但是matrix还有后面两个参数,如果加上这两个参数就变成2 * 3的矩阵,这是无法相乘的。所以为了方便计算我们把它拼凑成3*3的矩阵:
在这个计算公式中, 矩阵最后一行是可以忽略的。所以对于任意一个点(x, y),应用matrix(a,b,c,d,e,f)变换后的坐标是
注: 关于线性代数的教程推荐这个