fabric setViewportTransform 矩阵推导

43 阅读1分钟

平移公式推导

[a11a12a13a21a22a23a31a32a33][xy1]=[x+dxy+dy1] \begin{bmatrix} a_{11} & a_{12} & a_{13} \\ a_{21} & a_{22} & a_{23} \\ a_{31} & a_{32} & a_{33} \end{bmatrix} * \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} = \begin{bmatrix} x + dx \\ y + dy \\ 1 \end{bmatrix}

根据 a11 * x + a12 * y +a13 = x + dx, 可得出 a11 = 1, a12 = 0, a13 = dx, 以此类推, 左侧矩阵应为

[10dx01dy001] \begin{bmatrix} 1 &0 & dx \\ 0 &1 & dy \\ 0 &0 & 1 \end{bmatrix}

平移代码

const transform = [1, 0, 0, 1, dx, dy]
canvas.setViewportTransform(transform)

缩放公式推导

[a11a12a13a21a22a23a31a32a33][xy1]=[sxxsyy1] \begin{bmatrix} a_{11} & a_{12} & a_{13} \\ a_{21} & a_{22} & a_{23} \\ a_{31} & a_{32} & a_{33} \end{bmatrix} * \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} = \begin{bmatrix} sx * x \\ sy * y \\ 1 \end{bmatrix}

根据 a11 * x + a12 * y +a13 = sx * x, 可得出 a11 = sx, a12 = 0, a13 = 0, 以此类推, 左侧矩阵应为

[sx000sy0001] \begin{bmatrix} sx &0 & 0 \\ 0 &sy & 0 \\ 0 &0 & 1 \end{bmatrix}

缩放代码

const transform = [sx, 0, 0, sy, 0, 0]
canvas.setViewportTransform(transform)

旋转公式推导

假设半径为 r = 1,向量夹角为α, 那么初始点坐标为( cos(α), sin(α)), 旋转θ后, 其点坐标为( cos(α + θ), sin(α + θ)), 根据和角公式

cos(α + θ) = cos(α)cos(θ) - sin(α)sin(θ)

sin(α + θ) = sin(α)cos(θ) + cos(α)sin(θ)

[a11a12a13a21a22a23a31a32a33][cos(α)sin(α)1]=[cos(α)cos(θ)sin(α)sin(θ)sin(α)cos(θ)+cos(α)sin(θ)1] \begin{bmatrix} a_{11} & a_{12} & a_{13} \\ a_{21} & a_{22} & a_{23} \\ a_{31} & a_{32} & a_{33} \end{bmatrix} * \begin{bmatrix} cos(α) \\ sin(α) \\ 1 \end{bmatrix} = \begin{bmatrix} cos(α)cos(θ) - sin(α)sin(θ) \\ sin(α)cos(θ) + cos(α)sin(θ) \\ 1 \end{bmatrix}

根据 a11 * cos(α) + a12 * sin(α) +a13 = cos(α)cos(θ) - sin(α)sin(θ), 可得出 a11 = cos(θ) a12 = - sin(θ ), 0 以此类推, 左侧矩阵应为

[cos(θ)sin(θ)0sin(θ)cos(θ)0001] \begin{bmatrix} cos(θ) &-sin(θ) & 0 \\ sin(θ) &cos(θ) & 0 \\ 0 &0 & 1 \end{bmatrix}

旋转代码

const transform = [cos,  sin,  -sin,  cos,  0,  0]
canvas.setViewportTransform(transform)

旋转进阶

然后在实际应用中, 按照以上代码去执行, 其结果是很奇怪的,原因是其旋转运动是围绕画布左上角(0, 0)位置做旋转的, 而我们普遍的需求是围绕画布中心做旋转,为了达到目的, 还需要额外做两步, 总过程是 平移到中心 ⇒ 旋转 ⇒ 再平移回来, 这过程中一共会做两次次矩阵相乘, 假设当前画布中心距离左上角中心点的距离为(x, y)

  1. 平移到中心
[10x01y001] \begin{bmatrix} 1 &0 & -x\\ 0 &1 & -y \\ 0 &0 & 1 \end{bmatrix}
  1. 旋转
[cossin0sincos0001][10x01y001]=[cossincosx+sinysincossinxcosy001] \begin{bmatrix} cos &-sin & 0 \\ sin &cos & 0 \\ 0 &0 & 1 \end{bmatrix} * \begin{bmatrix} 1 &0 & -x\\ 0 &1 & -y \\ 0 &0 & 1 \end{bmatrix} = \begin{bmatrix} cos &-sin & -cos*x + sin*y \\ sin &cos & -sin* x - cos * y \\ 0 &0 & 1 \end{bmatrix}
  1. 平移回原点
[10x01y001][cossincosx+sinysincossinxcosy001]=[cossinxcosx+sinysincosysinxcosy001] \begin{bmatrix} 1 &0 & x\\ 0 &1 & y \\ 0 &0 & 1 \end{bmatrix} * \begin{bmatrix} cos &-sin & -cos*x + sin*y \\ sin &cos & -sin* x - cos * y \\ 0 &0 & 1 \end{bmatrix} = \begin{bmatrix} cos &-sin & x -cos*x + sin*y \\ sin &cos & y -sin* x - cos * y \\ 0 &0 & 1 \end{bmatrix}

以画布中心旋转代码

const transform = [cos,  sin,  -sin,  cos,  x - cos * x + sin * y,  y - sin * x - cos * y]
canvas.setViewportTransform(transform)