css,矩阵,相信大家对这两个词都不会陌生吧,但如果说css中也有矩阵,恐怕就很少有人知道或者用到过了。
先来说一下css3吧,这个新标准已经发布很久了,现在无论是PC还是移动上都随处可以看到它的身影,如border-radius, box-shadow, text-shadow, flex-box等等,它能够帮助我们用很简洁的样式代码做出在以前需要图片才能达到的效果。
transform是css3中一个很好用的属性,它允许我们移动(translate)、旋转(rotate)、放缩(scale)、扭曲(skew)元素,同时它还可以实现3d效果。
说到现在为止,好像还没看到矩阵(⊙_⊙)?嘿嘿,其实它就隐藏在上面的transform中,
你一定用css3写过这样的代码:
-webkit-transform: translate(100px, 100px) rotate(90deg) scale(2)
但你可能不知道它还有另外一种写法:
-webkit-transform: matrix(a,b,c,d,e,f)
没错,这就是css中的矩阵,一定有很多人看到这里就流汗了...明显第一种写法更简单易懂啊,为什么我还要学习第二种呢?
原因嘛,当然还是来源于需求中了...
用css3的transition做动画即简单,同时性能也比js要好,但它有一个小小的缺陷,就是如果让动画在中途的某个位置(不确定的)停下来,那么当前动画运到到哪里了就成了一个问题,因为元素的css样式已经被设成了动画结束的目标值,而且也没有类似touchmove这种持续触发的事件。后来我尝试用getComputedStyle去获取transform的值然后在设置到元素的style中,果然动画停下来了,哈哈哈,我果然是个天才(原谅我很喜欢樱木-。-)
但紧接着问题又来了,获取到的值是matrix(...),妈呀,what is it,如果需要继续旋转可怎么办啊?
带着这个问题我又重新翻出了大学时候的几何代数,好厚一层灰...不过仔细看完后发现还是很有收获的~
假定元素上某一点变换前的坐标是(x, y),那么上述变换都可以通过下面这个矩阵的乘法来表示
通过上图的矩阵运算我们可以得到变换后的元素的坐标
x‘ = ax + cy + e;
y‘ = bx + dy + f;
看到这里我想很多人一定都明白了。
- 移动
当a=d=1且b=c=0时,
x‘ = x + e;
y‘ = y + f;
这也就是我们通常所写的translate(e, f),换句话说
transform:tanslate(e, f) = transform : matrix(1,0,0,1,e,f)
- 放缩
当b=c=e=f=0时,
x‘ = ax;
y‘ = dy;
这也就是我们通常所写的scale(a, d),换句话说
transform:scale(a, d) = transform : matrix(a,0,0,d,0,0)
- 旋转
当a=cosθ,b=sinθ, c = -sinθ, d = cosθ时,
x‘ = cosθx - sinθy;
y‘ = sinθx + cosθy;
这也就是我们通常所写的rotate(θdeg),换句话说
transform:rotate(θdeg) = transform : matrix(cosθ,sinθ,-sinθ,cosθ,0,0)
(注:这里用极坐标系简单的推导一下就可以得到)
可惜,知道这些还并没有结束,因为同一个矩阵可能对应着不同变换的组合,浏览器会以队列的方式去渲染transform,简单的说就是写在前面的会影响到写在后面的。
举个例子:
transform:matrix(2,0,0,2,100,100);
它等效于
transform : translate(100px, 100px) scale(2);
transform : scale(2) translate(50px, 50px);
这两种写法和一个matrix是一样的,所以如果你拿到了一个matrix,不一定能得到你想要的位移、放缩和旋转的值,你必须知道它们是哪一种顺序组合起来的。
知道顺序之后也挺头疼的,需要解方程,这里需要细心,弄错一个就gg...
回到我在项目中的遇到的实际问题:
有一个元素动画暂停的时候我拿到了matrix的值matrix(a,b,c,d,e,f),而且我知道transform属性值的顺序是
rotate->scale->translate
下面我们开始解方程吧....
先是旋转,
x‘ = cosθx - sinθy;
y‘ = sinθx + cosθy;
再是放缩和位移,
x" = x‘*scale + tx;
y" = y‘*scale + ty;
代入后很容易得到
scale =
rotate = arcos(a/scale)
将得到的rotate转换成角度值然后根据b的符号去判断是否rotate要加上180,
相比而言translate就比较麻烦了,这里先通过前面的推导先明确tx=e, ty =f,translate的值是在rotate和scale的作用下变成tx和ty的,
因此有
atx‘ + cty‘ = tx;
btx‘ + dty‘ = ty;
解得
tx‘ =
ty‘ =
tx‘和ty‘也就是我们希望得到的translate的值。
当然如果你不需要知道具体的值,只是在此基础上继续变换的话,那么只需要再左乘一个后续的变换矩阵就可以了,以此类推。
发现说了半天,自己也有点晕了,总结一下吧,css3中的transform其实还有一种不太常用的矩阵写法,但它的确很有用,它可以帮助我们更深入的了解变换的过程,理解变换的叠加,同时也能实现一些很复杂的变换,如扭曲、倒影等(在后面的文章中我会详细说到)。
这里还有一个小小的思考,浏览器层面在渲染时计算元素的位置时应该就是使用的矩阵吧,所以如果我们在css样式中直接使用矩阵的写法是不是更高效一些呢,当然我知道这种程度的计算量对于浏览器是可以忽略的,哈哈~
不早了,洗洗睡了~