CSS3矩阵matrix进行2D变换的数学原理
css3的2D转换中matrix接受6个参数,却可以实现平移、旋转、放缩、斜切四种效果。它是如何做到的呢?
此处的你有两个选择
0 - 对自己有较高要求!老老实实静下心来将本文看完,
1 - 直接看总结——点我直达,只学习如何使用matrix
预备知识
:矩阵相乘、三角函数,fighting!
此处附上矩阵相乘的百度百科
定义
我们先将matrix接受的六个参数记为a,b,c,d,e,f,则该变换矩阵记为
二维平面上一个点记为(x,y),为了与向量
区分开,我们使用数字1
代表点,0
代表向量,则二维平面上一个点应记为(x,y,1),为了使该点经过变换后依旧为(x,y,1)的形式,矩阵可以改为
进行相乘变换(自行百度矩阵相乘的公式)
我们得到了一个新的点(ax+cy+e,bx+dy+f,1)
怎么将这个冷冰冰的点和我们的平移旋转缩放斜切联系到一起呢?
平移
我们知道,对一个点(x,y,1)向x轴正向平移10,向y轴正向平移20,得到的点为(x+10,y+20,1),而经过矩阵变换的点为(ax+cy+e,bx+dy+f,1),对比得到a=1,c=0,e=10,b=0,d=1,f=20
所以变换矩阵为
1 | transform: matrix(1,0,0,1,10,20); // a=1,c=0,e=10,b=0,d=1,f=20 |
显然,与平移变换直接相关的参数为e、f
参数!!
当我们只进行平移变换时,只需要这样
1 | transform: matrix(1,0,0,1,e,f); // 相当于 transform: translate(e,f); |
放缩
假设我们对一个边长为10px的正方形放大2倍,如图所示
对左上角的那个点来说,它由(0,10,1)变为(0,20,1)
对任意一个点(x,y,1)来说,放大后得到的点为(2x,2y,1),而经过矩阵变换的点为(ax+cy+e,bx+dy+f,1),对比得到a=2,c=0,e=0,b=0,d=2,f=0
所以变换矩阵为
1 | transform: matrix(2,0,0,2,0,0); // a=2,c=0,e=0,b=0,d=2,f=0 |
显然,与缩放变换直接相关的参数为a、d
参数!!
当我们只进行平移变换时,只需要这样
1 | transform: matrix(a,0,0,d,0,0); // 相当于 transform: scale(a,d) |
聪明的你现在肯定猜到了,如果既进行平移,又进行缩放,则transform: matrix(a,0,0,d,e,f);
即可,a、d表示缩放,e、f表示平移
旋转
剩下两个参数b、c,可是还有旋转和斜切没有实现呢!(冒汗
旋转的话,涉及到旋转角度的问题啦。记为θ。
复习一波三角函数,以下使用极坐标
表示
来一波推理
对任意一个点(x,y,1)来说,旋转θ°后得到的点为(xcosθ-ysinθ,xsinθ+ycosθ,1),而经过矩阵变换的点为(ax+cy+e,bx+dy+f,1),对比得到a=cosθ,b=sinθ,c=-sinθ,d=cosθ,e=0,f=0
所以变换矩阵为
这里用到了a、b、c、d四个参数,前面我们知道缩放用了a、d两个参数,如果我们既要旋转又要缩放怎么办呢??
已经证明:任何二维组合变换均可分解为多个基本变换的乘积
故我们只需要分别求出变换矩阵,再相乘
即可。
1 | q=DCBAp => q=(DCBA)p=Mp |
解释:p表示当前的点,ABCD表示四种变换,上述式子的变换顺序为A->B->C->D
斜切
先沿x轴扭曲
有以下式子
与(ax+cy+e,bx+dy+f,1)对比得出a=1,c=tanθ,e=0,b=0,d=1,f=0
所以变换矩阵为
再看沿y轴扭曲
变换矩阵为
当l种变换同时进行时,有
【思考】我们之前说过,任何二维组合变换均可分解为多个基本变换的乘积,为什么这里不是将两个扭曲矩阵相乘呢?(先沿x轴扭曲,再沿y轴扭曲?
emmm,是这样的,我们这里的计算都是相对最初始
的图形,如果用两个矩阵相乘,则是使初始图形沿x轴扭曲θx度后得到的新图形
再对y轴扭曲θy度!!如果要达到我们想要的效果,就只能重新计算沿y轴的扭曲角度!!
【总结】
至此我们已经把2D变换的数学原理挖出来了,写一个用法总结,便于自己查找也便于大家查看
- 平移,只需要控制最后两个参数e、f,a、d参数为1
1
2
3transform: matrix(1,0,0,1,e,f);
// 等同于
transform: translate(e,f); - 放缩,只需要控制a、d参数,其它为0
1
2
3transform: matrix(a,0,0,d,0,0);
// 等同于
transform: scale(a,d); - 旋转,只需要控制a、b、c、d参数
1
2
3transform: matrix(cosθ, sinθ, -sinθ, cosθ);
// 等同于
rtransform: rotate(θdeg) - 斜切
1
2
3transform: matrix(1, tanθy, tanθx, 1, 0, 0);
// 等同于
transform: skew(θx, θy); - 复合变换
如以下这种类型,先旋转再缩放再扭曲,如何用矩阵得到呢?1
transform: rotate(360deg) scale(2,2) skew(10deg,5deg);
已经证明:任何二维组合变换均可分解为多个基本变换的乘积
故我们先求出旋转矩阵A,缩放矩阵B和斜切矩阵C
最后得到变换矩阵M = CBA = C(B(A))
【什么时候我们用矩阵?】
思考下,既然已经有rotate、scale等函数,直接调用不就行了吗??为什么人类要跟自己过不去,手算矩阵???
大部分情况下,当然是使用那些基本的rotate、scale函数就好了,下面这种类型的情况呢?
- 需求说:请把这些点沿着y轴取对称。 你:简单
x’ = ax+cy+e = -x,y’ = bx+dy+f = y嘛,一番心算后你得到1
transform: matrix(-1,0,0,1,0,0);
- 需求说:算了,还是对y=x取对称吧。你:简单,先整体旋转
逆时针
旋转45°,再对y轴取对称,再整体顺时针
旋转45°(45°)就好了嘛~
(上图右侧表示逆时针旋转)用我们的高中数学来验证一波,点(a,b)关于y=x轴取对称,得到的点应该为(b,a)1
transform: matrix(0,1,1,0,0,0);
验证正确! - 需求犹豫了0.5s,哎呀还是对y=kx取对称吧…
- 需求摇摇头说,对y=kx+b取对称吧….
此处插播高中知识——如何关于一条直线取对称点呢?
- 两个点关于一条直线对称,那么这两个点的连线的中点必在该直线上
- 两个点的连线所在直线的斜率与该直线斜率的乘积为-1(因为垂直)
- 可以列出两个方程,求出对称点!
- 对称点都求出来了,矩阵也就出来了~
- 现在请看官使用rotate、scale、skew、translate实现上述需求~
【demo】使用css3画一个立方体
【参考】
https://www.atjiang.com/css-transform-skew-angle/
http://0313.name/archives/66
https://www.zhangxinxu.com/wordpress/2012/06/css3-transform-matrix-%E7%9F%A9%E9%98%B5/
http://www.wukai.me/2015/10/22/css3-cube/