decomposing css 2d transform matrix into simple transformations

原理

css transform 的基本原理是在二维空间上做矩阵变换,即对每个点作用以下矩阵

其中变换矩阵可以通过 matrix(a,b,c,d,e,f) 来设置给 css 的 transform 属性。

如果觉得 matrix 太复杂,也可以通过语义化的最简变化原语来实现,例如


.x {

transform: 'translate(tx,ty) rotate(a) skew(b) scale(sx.sy)'

}

来达到同样的效果,代表将坐标系先 translate 然后 rotate 然后 skew 然后 scale 为新的坐标系。即新坐标系下的点先经过 scale 然后 skew 然后 rotate 然后 translate 后对应于老坐标系下的点。

本质上每个最简变化原语对应于一个简单的变换矩阵,多个变换叠加即形成最终矩阵:

由于矩阵乘法不符合交换律,因此最简变换原语顺序也不可变,例如


.x {

transform: 'translate(tx,ty) rotate(a)'

}

.y {

transform: 'rotate(a) translate(tx,ty)'

}

.x 和 .y 渲染效果并不一致.

动画

但对 transform 进行动画时,例如浏览器内部的 transition ,如果初值和最终值都是最简变换,则很简单,只需要在初值和最终值之间插值即可,例如


.x {

transform: 'translate(0,0) rotate(0)'

}

.y {

transform: 'translate(100px,100px) rotate(100deg)'

}

那么当一个元素 10 秒内从 .x 线性变换到 .y,那么第 t 秒时的样式为


.t {

transform: 'translate(t*100/10 px,t*100/10 px) rotate(t*100/10 deg)'

}

注意:其中插值方式 css3 不止有线性变换.

分解矩阵为最简变换

但如果元素初始或者最终值 transform 为 matrix 形式或者变换原语不对应,例如


.x {

transform: 'translate(0,0) rotate(0)'

}

.y {

transform: 'rotate(100deg) translate(100px,100px)'

}

怎么插值呢, css3 规范规定必须先要把最终 matrix 分解后转换成一系列固定顺序的简单变换,然后再对每个简单变化进行以上的插值处理。

其中分解矩阵用到的算法为经典图像处理书籍 graphics gems 中的算法, 具有一定的矩阵论知识(貌似是行列式变换),本文以下通过直观的公式推导来得出对应于简单变化的一系列简单矩阵中的元素,进而可以很容易推导出简单变化,再进行插值处理。

其中 右侧的 abcdef 为已知值,tx,ty 为便宜位置(translate(tx,ty)),alpha 为 旋转角度(rotate(alpha)),beta 为沿 x 轴的倾斜角度 skewX(beta) , sx,sy 为缩放系数(scale(sx,sy))。

通过方程计算即可得出简单变化矩阵中的参数,进而可以得出简单变化具体形式,那么插值就很容易了。进一步的 3d transform 也可以同样由此推出,不过如果理解了矩阵论就更加容易了。

demo 演示

通过 js 实现和浏览器原生一致的矩阵分解

最后,你懂 transform 了么,我突然不懂矩阵了 🙂

decomposing css 2d transform matrix into simple transformations”的一个响应

  1. Pingback: 2d transform in canvas | {focus: web}

留下评论