前排提示:本篇为该系列第二篇,内容相对于第一篇来说比较简单,各位可当休闲读物来看。

《前端 · 深入理解 transform 函数的计算原理 ①》

接上回书讲到,我们知道了 transform 函数中的四大类共九个计算函数的工作原理。

既然除去 matrix 以外的八个具体形变函数,都可以用 matrix 来表达,那么这样是否意味着,只要有具体的参数,就可以用 matrix 来替代其他所有计算函数。也就是说,其实只用 matrix 就可以玩转 transform

那么怎么玩?

在本篇,我们要做的目标只有一个:代码压缩。

展开来讲,是针对 transform 函数的代码压缩。

首先,为什么要做这件事情呢?

原因有二:
第一,transform 函数支持函数接续使用,所以经常可以看到开发者在代码中写入非常长的函数链条,诸如:

transform="rotate(-10 50 100) translate(-36,45.5) matrix(1,2,3,4,5,6) skewX(40) scale(1 0.5)"

其实这一长串要做的事情只有一个,那就是把一个元素,从状态 A,变成状态 B

当元素数量上来以后,这种冗长的代码写法,无异于对加载和渲染产生额外消耗。而且代码阅读起来也十分不美观。

上一篇讲过,transform 中的多函数工作原理为矩阵乘法,所以这么长一串,其实只需要一个 matrix 就可以解决(这里保留了两位小数):

transform="matrix(1.33,1.80,2.38,2.46,-38.19,66.30)"

是不是看起来舒服多了?

交给浏览器的运算步骤减少了,前端代码文件体积降低了,视觉上看起来也规整很多,一举多得。

第二,显式的 transform 函数序列很容易让很多不喜欢动脑的“致敬者”轻易获取到变换逻辑。而由于矩阵乘法运算难以倒推出结果,这种隐式的压缩写法,等同于加密处理。

综上,我们要做的,其实就是完成矩阵乘法。

方法很简单:
① 将各个变化函数转化为对应的 matrix 形式
② 将各个 matrix 按顺序连乘,得到最终结果

最后这个有且仅有一个的变化矩阵,就是我们用来替换“函数链”的那个唯一的 matrix()

第一步就不多说了,上一篇讲地非常细。关键在于第二步,不过矩阵乘法本身,对于理工科学生来说,大学一年级甚至高中三年级都学过,其实也不难。

这里给广大读者展开列一下公式,直接套用即可:

$$\begin{aligned}M_{result} &= M_{1}·M_{2} \\&= \begin{pmatrix}a_{1} & c_{1} & e_{1} \\b_{1} & d_{1} & f_{1} \\0 & 0 & 1 \\\end{pmatrix}·\begin{pmatrix}a_{2} & c_{2} & e_{2} \\b_{2} & d_{2} & f_{2} \\0 & 0 & 1 \\\end{pmatrix} \\&= \begin{pmatrix}a_{1}*a_{2}+c_{1}*b_{2}+e_{1}*0 & a_{1}*c_{2}+c_{1}*d_{2}+e_{1}*0 & a_{1}*e_{2}+c_{1}*f_{2}+e_{1}*1 \\b_{1}*a_{2}+d_{1}*b_{2}+f_{1}*0 & b_{1}*c_{2}+d_{1}*d_{2}+f_{1}*0 & b_{1}*e_{2}+d_{1}*f_{2}+f_{1}*1 \\0*a_{2}+0*b_{2}+1*0 & 0*c_{2}+0*d_{2}+1*0 & 0*e_{2}+0*f_{2}+1*1 \\\end{pmatrix} \\&= \begin{pmatrix}a_{1}*a_{2}+c_{1}*b_{2} & a_{1}*c_{2}+c_{1}*d_{2} & a_{1}*e_{2}+c_{1}*f_{2}+e_{1} \\b_{1}*a_{2}+d_{1}*b_{2} & b_{1}*c_{2}+d_{1}*d_{2} & b_{1}*e_{2}+d_{1}*f_{2}+f_{1} \\0 & 0 & 1 \\\end{pmatrix} \\\end{aligned}$$

python 写,核心就是这一行:

m_result = {'a': m1['a'] * m2['a'] + m1['c'] * m2['b'], 'b': m1['b'] * m2['a'] + m1['d'] * m2['b'],
            'c': m1['a'] * m2['c'] + m1['c'] * m2['d'], 'd': m1['b'] * m2['c'] + m1['d'] * m2['d'],
            'e': m1['a'] * m2['e'] + m1['c'] * m2['f'] + m1['e'],
            'f': m1['b'] * m2['e'] + m1['d'] * m2['f'] + m1['f']}

初始默认为 matrix(1, 0, 0, 1, 0, 0),只要将每个函数接续连乘,就可以最终计算出结果。

实测结果如下,各位可自行验证:

<svg width="100%" height="100%">
   <circle cx="100" cy="100" r="10" fill="red" transform="rotate(-10 50 100) translate(-36,45.5) matrix(1,2,3,4,5,6) skewX(40) scale(1 0.5)"></circle>
   <circle cx="100" cy="100" r="10" fill="green" transform="matrix(1.33,1.80,2.38,2.46,-38.19,66.30)"></circle>
</svg>

其实代码压缩在前端领域非常常见,不同语言也有不同的压缩方式。出于一个比较重要的细节处理,撰写此文跟大家分享。

好啦,本篇就先这样。

03-05 22:36