深入理解CSS 中 transform matrix矩阵变换问题
一、概述
css中transform属性中的translate、scale、rotate、skew变换属性均是通过matrix矩阵变换实现的。直接使用矩阵变换,实现位移、缩放、旋转、倾斜等动画,不够直观;实际开发中还是使用变换属性多一点。但这一点都不影响matrix属性的重要性;理解matrix属性定义的参数,需要一些线性代数的基础。
二、矩阵
1、矩阵的定义
将一些元素排列成若干行,每行放上相同数量的元素,就是一个矩阵。如:
2、矩阵的基本运算
加(减)法:
两个矩阵的加减法,取每个元素的对应的和(差)
数乘:
数字与矩阵元素之间的对应的乘积
矩阵乘法:
仅当第一个矩阵的列数(column)和第二个矩阵的行数(row)相等时才能定义。运算规则是,第一个矩阵行元素 与 第二个矩阵中的列元素,分别相乘求和,得到对应元素。例如 第一个矩阵的第一行元素[1 0 2],与第二个矩阵的第一列元素[3,2,1],分别相乘求和,即:1 x 3 + 0 x 2 + 2 x 1 = 5;得到运算后矩阵的左上第一个元素。
3、向量
物理学称为矢量,指具有大小(magnitude)和方向的量。它可以形象化地表示为带箭头的线段。
向量的分解:
直角坐标系中,任意向量可表示为:其中,i、j为单位向量。
4、矩阵与向量
矩阵中的元素可以看做是一组坐标,而在平面直角坐标系中,一组向量可以使用坐标表示,因此可以使用矩阵表示一组向量,而对矩阵的运算,可以看做是对一组坐标的变换。来看具体的例子。
三、矩阵变换
1、矩阵缩放
建立一个特殊的平面直角坐标系,坐标系的特殊之处在于x轴和y轴是橡皮筋构成的,可以进行任意拉伸和收缩。坐标系中存在向量a(-1,1);考虑如何将其放大一倍。
坐标系是弹性的,显然只需要将坐标系拉伸一倍就可以使得向量a放大一倍。根据公式有:
拉伸坐标系实际上改变的是单位向量,拉伸后的坐标系单位向量均为原先的两倍,即:
通过缩放单位向量,使得坐标系中的向量发生缩放,这是对于单一向量而言的。对于页面上的由多个坐标构成的块状 div 而言,缩放单位向量最直观的效果就是长度和宽度的缩放。
2、矩阵的旋转
如何使坐标系中的向量发生旋转,考虑将向量a(-1,1),顺时针旋转45度。
很显然需要旋转单位向量。有:
结果:
对a向量旋转可以通过旋转单位向量实现,通过对单位向量的旋转实现对任意向量的旋转。常见的变换操作,如缩放,旋转,倾斜,都可以通过对单位向量的操作进行实现。css matrix函数提供的参数就是描述一组单位向量的矩阵。
四、css中的矩阵变换
css 中 transform matrix 2d变换的参数一共有6个:
matrix(a, b, c, d, e, f)
其中默认参数为:
matrix(1, 0, 0, 1, 0, 0)
其中前4个参数,就是单位向量i(1,0)、j(0,1)。但注意,web页面中的坐标系原点在左上角,向右和向下对应平面直角坐标系的x轴和y轴正向;因此与平面直角坐标系的y轴的方向是相反的。
观察 transform 属性中的scale、rotate、skew是如何通过matrix矩阵来实现。
1、scale
使用scale缩放一个div的css是这样描述的:表示将宽和高同时放大两倍。
transform: scale(2); //同transform: scale(2,2)
使用矩阵达到上述效果,如果使用matrix实现,将单位向量放大两倍即可
transform: matrix(2,0,0,2,0,0);
2、rotate
使用rotate顺时针旋转div 30deg:
transform: rotate(30deg);
使用matrix实现,只需旋转单位向量即可
transform: matrix(cos30°, sin30°, -sin30°, cos30°, 0, 0);
transform: matrix(0.866, 0.5, -0.5, 0.886 ,0 , 0);
3、skew
使用skew 倾斜30度:
transform: skew(30deg, 0);
使用matrix实现:
transform: matrix(1,0,0.5773502691896257,1,0,0);
4、translate
上述3中变换均通过变换单位向量产生,原点都未发生变化。位移变化需要增加矩阵元素,和函数的参数,也是matrix(a, b, c, d, e, f)中,e、f参数的作用,矩阵的形式可以变现为以下形式:
如,坐标[-1,-1],向右向上平移2个单位,得到变换后的坐标[1, 1]。
下面的css效果是一样的:
transform: translate(50px,0);
transform: matrix(1,0,0,1,50,0);
使用matrix的形式,可以一次性定义上述4种变换。但使用transform, 需要将变换表达式写在一行,使用空格分隔有助于阅读,但不是必须的。
transform: skew(30deg,0) scale(2) rotate(30deg) translate(50px);
5、matrix3d
matrix3d(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) //定义 3d 转换,使用 16 个值的 4x4 矩阵。
矩阵的形式为:
6、2d变换的矩阵形式
给出一组transform变换(rotate、skew、translate、scale),将其转换成matrix函数的参数形式,需要注意以下几点:
1、rotate与skew变换是相互影响的,试图直接通过三角函数的计算得出相应的参数是不正确的,需要将其带入矩阵进行运算;
2、scale与translate变换理论上可以直接修改矩阵元素,也可以带入矩阵运算,但反复测试发现,translate带入矩阵后运算结果有误,这里直接修改矩阵元素。
3、矩阵运算比较复杂,使用的 sylvester.js 库。以下是代码
let transformstring = 'skew(15deg) rotate(30deg) scale(1.5) translate(30px)' function stringtomatrix(transformstring) { try { // 参数检查 if (typeof transformstring !== 'string') { console.error('params must be string'); return; } if (transformstring.length === 0) { console.error('wrong transform format'); return } if (['x', 'y', '3d'].some(item => transformstring.includes(item))) { console.error('3d transform unsupported yet'); return; } let a = 1, b = 0, c = 0, d = 1, e = 0, f = 0; let reg = /(scale|rotate|skew|translate){1}\(.*?\)/g; let matrixdefault = $m([ [1, 0, 0], [0, 1, 0], [0, 0, 1] ]); let matrixscale, matrixrotate, matrixskew, matrixtranslate, matrixresult; transformstring.match(reg).foreach(item => { let re = /\d+(.\d+)?/g; if (item.includes('rotate')) { let params = item.match(re); if (params) { // a = math.cos(params[0] / 180 * math.pi) * a; //需要带入矩阵 // b = math.sin(params[0] / 180 * math.pi) * a; // c = -math.sin(params[0] / 180 * math.pi) * d; // d = math.cos(params[0] / 180 * math.pi) * d; const i = params[0] / 180 * math.pi; matrixrotate = $m([ [math.cos(i), -math.sin(i), 0], [math.sin(i), math.cos(i), 0], [0, 0, 1] ]) matrixresult = matrixresult ? matrixresult.x(matrixrotate) : matrixdefault.x(matrixrotate); } } if (item.includes('skew')) { let params = item.match(re); // to fix 角度不可超过90度 // params[0] ? c = math.tan(params[0] / 180 * math.pi) : ''; // params[1] ? b = math.tan(params[1] / 180 * math.pi) : ''; const [i = 0, j = 0] = params.map(a => parsefloat(a)); // matrixskew = [1, math.tan(j), math.tan(i), 1, 0, 0];//需要带入矩阵 matrixskew = $m([ [1, math.tan(i / 180 * math.pi), 0], [math.tan(j / 180 * math.pi), 1, 0], [0, 0, 1] ]); matrixresult = matrixresult ? matrixresult.x(matrixskew) : matrixdefault.x(matrixskew); } if (item.includes('scale')) { let params = item.match(re); // a = params[0] ? params[0] * a : a; // d = params[1] ? params[1] * d : params[0] * d; let [i, j = i] = params.map(a => parsefloat(a)); matrixscale = $m([ [i, 0, 0], [0, j, 0], [0, 0, 1] ]); matrixresult = matrixresult ? matrixresult.x(matrixscale) : matrixdefault.x(matrixscale); // if(matrixresult) { // matrixresult.elements[0][0] = i * matrixresult.elements[0][0]; // matrixresult.elements[1][1] = i * matrixresult.elements[1][1]; // }else { // matrixdefault.elements[0][0] = i * matrixdefault.elements[0][0]; // matrixdefault.elements[1][1] = i * matrixdefault.elements[1][1]; // } } if (item.includes('translate')) { let params = item.match(re); let [x = 0, y = 0] = params.map(a => parsefloat(a)); e = x; f = y; matrixtranslate = $m([ [1, 0, x], [0, 1, y], [0, 0, 1] ]); if (matrixresult) { matrixresult.elements[0][2] = x; matrixresult.elements[1][2] = y; } else { matrixdefault.elements[0][2] = x; matrixdefault.elements[1][2] = y; } // matrixresult = matrixresult ? matrixresult.x(matrixtranslate): matrixdefault.x(matrixtranslate); } }) const [[a1, a2, a3], [b1, b2, b3], [c1, c2, c3]] = matrixresult.elements; return `matrix(${a1}, ${b1}, ${a2}, ${b2}, ${a3}, ${b3})`; } catch (error) { console.log(error) } } stringtomatrix(transformstring) // matrix(1.5, 0.7499999999999999, -0.401923788646684, 1.299038105676658, 30, 0)
参考连接:
1、https://zh.wikipedia.org/wiki/%e7%9f%a9%e9%98%b5#%e6%a0%87%e8%ae%b0
2、http://www.ruanyifeng.com/blog/2015/09/matrix-multiplication.html
3、https://www.jianshu.com/p/dcf189998ae2
到此这篇关于深入理解css 中 transform matrix矩阵变换的文章就介绍到这了,更多相关css 中 transform matrix矩阵变换内容请搜索以前的文章或继续浏览下面的相关文章,希望大家以后多多支持!