变换矩阵 transform

Canvas transform方法详解,掌握变换矩阵原理、倾斜效果和高级变换技巧。transform方法提供了最底层的矩阵变换能力,可以实现所有其他变换效果。

基本语法

ctx.transform(a, b, c, d, e, f)
ctx.setTransform(a, b, c, d, e, f)
参数说明
a水平缩放
b垂直倾斜
c水平倾斜
d垂直缩放
e水平平移
f垂直平移

变换矩阵结构

Canvas使用3x3的变换矩阵:

| a  c  e |
| b  d  f |
| 0  0  1 |

坐标变换公式:

x' = a*x + c*y + e
y' = b*x + d*y + f

transform vs setTransform

方法说明
transform()在当前变换基础上叠加新变换
setTransform()重置变换矩阵后设置新变换
// transform累积
ctx.transform(1, 0, 0, 1, 100, 0)  // 平移100
ctx.transform(1, 0, 0, 1, 50, 0)   // 再平移50,总共150

// setTransform重置
ctx.setTransform(1, 0, 0, 1, 100, 0)  // 平移100
ctx.setTransform(1, 0, 0, 1, 50, 0)   // 重置后平移50,总共50

基本矩阵变换演示

矩阵变换效果

用矩阵实现基本变换

平移

ctx.transform(1, 0, 0, 1, tx, ty)
// 等价于
ctx.translate(tx, ty)

旋转

const angle = Math.PI / 4
ctx.transform(Math.cos(angle), Math.sin(angle), -Math.sin(angle), Math.cos(angle), 0, 0)
// 等价于
ctx.rotate(angle)

缩放

ctx.transform(sx, 0, 0, sy, 0, 0)
// 等价于
ctx.scale(sx, sy)

倾斜变换

倾斜(错切)是transform独有的能力:

// 水平倾斜
ctx.transform(1, 0, skewX, 1, 0, 0)

// 垂直倾斜
ctx.transform(1, skewY, 0, 1, 0, 0)

倾斜变换演示

倾斜变换效果

组合变换

使用矩阵组合多个变换:

// 组合:缩放 + 旋转 + 平移
const scale = 2
const angle = Math.PI / 4
const tx = 100
const ty = 50

ctx.transform(
  scale * Math.cos(angle),
  scale * Math.sin(angle),
  -scale * Math.sin(angle),
  scale * Math.cos(angle),
  tx,
  ty
)

组合变换演示

组合变换效果

获取当前变换矩阵

使用getTransform方法获取当前变换矩阵:

const matrix = ctx.getTransform()
console.log(matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f)

变换矩阵工具函数

const TransformMatrix = {
  identity() {
    return { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }
  },
  
  translate(tx, ty) {
    return { a: 1, b: 0, c: 0, d: 1, e: tx, f: ty }
  },
  
  rotate(angle) {
    const cos = Math.cos(angle)
    const sin = Math.sin(angle)
    return { a: cos, b: sin, c: -sin, d: cos, e: 0, f: 0 }
  },
  
  scale(sx, sy) {
    return { a: sx, b: 0, c: 0, d: sy, e: 0, f: 0 }
  },
  
  multiply(m1, m2) {
    return {
      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
    }
  },
  
  apply(ctx, m) {
    ctx.setTransform(m.a, m.b, m.c, m.d, m.e, m.f)
  }
}

矩阵变换动画

矩阵变换动画

注意事项

矩阵乘法顺序

transform的矩阵乘法是右乘,顺序与直觉相反。

累积效应

transform会累积,使用setTransform重置。

性能考虑

复杂变换时,直接使用矩阵比多次调用基本变换更高效。

精度问题

多次矩阵运算可能产生精度误差,定期重置。