使用 canvas 画布来操控图形或者图片的二维变化的方式一般是平移、缩放、和旋转。canvas 已相应提供了相关的 api。如:translate(), scale() 和 rotate(),但这些功能无法统一处理并且无法描述当前画布的处于某种状态。为了实现这种统一连贯性的状态描述,canvas 给我们提供了另外一种思路及方法,即使用:transform() / setTransform();因为画布上的每个对象都拥有一个当前的 3 x 3 变换矩阵,都可以使用一个 3 x 3 矩阵来描述当前状态,其实是 2 x 3 矩阵,但为了便于计算,人为添加第三行 0 0 1 变成 3 x 3 矩阵。费话不多话,下面直接看矩阵变换的已封装好的功能,为了便于读者理解,每一步都有详细解释及介绍。

function createMatrix()
{
    /**
     * 初始一个矩阵,开始时,a, d 为 1,其它为 0
     * a  c  e
     * b  d  f
     * 0  0  1
     *
     * a	水平缩放绘图
     * b	水平倾斜绘图
     * c	垂直倾斜绘图
     * d	垂直缩放绘图
     * e	水平移动绘图
     * f	垂直移动绘图
     *
     * 1  0  0
     * 0  1  0
     * 0  0  1
     *
     * */
    let m = []
    m[0] = 1
        m[1] = 0
        m[2] = 0
        m[3] = 1
        m[4] = 0
        m[5] = 0
        return m
}
function multiply(m1, m2)
{
    // 左乘
    /**
     * 已知矩阵 A 和矩阵 B,求 A 和 B 的乘积 C= AB
     * 常规方法:矩阵 C 中每一个元素 Cij = A 的第 i 行 乘以(点乘) B 的第 j 列
     *
     * m1[0]  m1[2]  m1[4]     m2[0]  m2[2]  m2[4]     m[0]  m[2]  m[4]
     * m1[1]  m1[3]  m1[5]  X  m2[1]  m2[3]  m2[5]  =  m[1]  m[3]  m[5]
     *  0      0      1          0      0      1        0      0     1
     *
     *
     * */
    let m = []
    m[0] = m1[0] * m2[0] + m1[2] * m2[1] + m1[4] * 0
        m[2] = m1[0] * m2[2] + m1[2] * m2[3] + m1[4] * 0
        m[4] = m1[0] * m2[4] + m1[2] * m2[5] + m1[4] * 1
        m[1] = m1[1] * m2[0] + m1[3] * m2[1] + m1[5] * 0
        m[3] = m1[1] * m2[2] + m1[3] * m2[3] + m1[5] * 0
        m[5] = m1[1] * m2[4] + m1[3] * m2[5] + m1[5] * 1
        return m
}
function translate(m, v)
{ // v 是个二维向量,即一个数组 [x, y],向量 v 是用于表示 x y 轴方向移动的距离,及其可表示移动的方向
    let arr = [...m]
    arr[4] = arr[4] + v[0]
        arr[5] = arr[5] + v[1]
        return arr
}
function rotate(m, rad)
{
    /**
     * 假如 A(X, Y) 点初始角度为 a,绕圆点旋转 θ 度,在坐标系上可得 B 点的坐标(X', Y'),算出半径为 r = √(X2 + Y2)
     * X' = cos(a + θ) * r
     * Y' = sin(a + θ) * r
     * 根据三角函数公式:cos(α + β) = cosαcosβ - sinαsinβ
     * 可得 X' = r * cosa * cosθ – r * sina * sinθ =  X * cosθ – Y * sinθ
     * 根据三角函数公式:sin(α + β) = sinαcosβ + cosαsinβ
     * 可得 Y' = sinacosθ * r + cosasinθ * r =  X * sinθ + Y * cosθ
     * X’       cosθ    -sinθ   0       X
     * Y’   =   sinθ    cosθ    0   X   Y
     * 1         0       0      1       1
     * 根据结合律结果应为:
     *
     * cosθ    -sinθ   0       m[0]  m[2]  m[4]      arr[0]  arr[2]  arr[4]
     * sinθ    cosθ    0   X   m[1]  m[3]  m[5]  =   arr[1]  arr[3]  arr[5]
     *  0       0      1       0      0      1         0       0        1
     *
     */
    return multiply(
        [
            Math.cos(rad),
            Math.sin(rad),
            -1 * Math.sin(rad),
            Math.cos(rad),
            0,
            0
        ],
        m)
}
function scale(m, v)
{ // v 是个二维向量,即一个数组 [scaleX, scaleY],向量 v 是用于表示 x y 方向的伸缩程度
    /**
     * 假如 X,Y 分别缩放(a, b)倍
     * X' = X * a
     * Y' = Y * b
     *
     * a    0    0       m[0]  m[2]  m[4]      arr[0]  arr[2]  arr[4]
     * 0    b    0   X   m[1]  m[3]  m[5]  =   arr[1]  arr[3]  arr[5]
     * 0    0    1       0      0      1         0       0        1
     *
     */
    return multiply([v[0], 0, 0, v[1], 0, 0], m)
}
function invert(m)
{
    // 矩阵求逆
    /**
     * 待定系数法求逆
     * X' = X * a
     * Y' = Y * b
     *
     * m[0]  m[2]  m[4]      arr[0]  arr[2]  arr[4]       1   0   0
     * m[1]  m[3]  m[5]  *   arr[1]  arr[3]  arr[5]   =   0   1   0
     * 0      0      1         0       0        1         0   0   1
     *
     * m[0] * arr[0] + m[2] * arr[1] + m[4] * 0 = 1    => arr[0] = (1 - m[2] * arr[1]) / m[0]
     * m[0] * arr[2] + m[2] * arr[3] + m[4] * 0 = 0    => arr[2] = (0 - m[2] * arr[3]) / m[0]
     * m[0] * arr[4] + m[2] * arr[5] + m[4] * 1 = 0    => arr[4] = (0 - m[2] * arr[5] - m[4]) / m[0]
     * m[1] * arr[0] + m[3] * arr[1] + m[5] * 0 = 0    => arr[0] = (0 - m[3] * arr[1]) / m[1]
     * m[1] * arr[2] + m[3] * arr[3] + m[5] * 0 = 1    => arr[2] = (1 - m[3] * arr[3]) / m[1]
     * m[1] * arr[4] + m[3] * arr[5] + m[5] * 1 = 0    => arr[4] = (0 - m[3] * arr[5] - m[5]) / m[1]
     */
    let arr = []
    arr[1] = 1 / m[0] / (m[2] / m[0] - m[3] / m[1])
        arr[0] = 0 - (m[3] * arr[1]) / m[1]
        arr[3] = 1 / m[1] / (m[3] / m[1] - m[2] / m[0])
        arr[2] = 0 - (m[2] * arr[3]) / m[0]
        arr[5] = (m[4] / m[0] - m[5] / m[1]) / (m[3] / m[1] - m[2] / m[0])
        arr[4] = 0 - m[3] * arr[5] - m[5] / m[1]
        return arr
}

使用上面的矩阵函数功能来操控画布画图

var screenWidth = $(window).width() > 750 ? 750 : $(window).width()
var screenHeight = $(window).height()
var c = document.getElementById('myCanvas_1')
c.setAttribute('width', screenWidth)
c.setAttribute('height', screenHeight)
var ctx = c.getContext('2d')
var m = createMatrix()
ctx.fillStyle = 'red'
ctx.fillRect(100, 100, 250, 100)
// translate
m = translate(m, [30, 10]) // 平移 30 10
ctx.setTransform(m[0], m[1], m[2], m[3], m[4], m[5])
ctx.fillStyle = 'orange'
ctx.fillRect(100, 100, 250, 100)
// rotate
m = rotate(m, (30 * Math.PI) / 180) // 旋转 30 度
ctx.setTransform(m[0], m[1], m[2], m[3], m[4], m[5])
ctx.fillStyle = 'yellow'
ctx.fillRect(100, 100, 250, 100)
// scale
m = scale(m, [0.7, 0.5]) // 缩放 0.7, 0.5
ctx.setTransform(m[0], m[1], m[2], m[3], m[4], m[5])
ctx.fillStyle = 'green'
ctx.fillRect(100, 100, 250, 100)
// invert 求逆
m = invert(m)
ctx.setTransform(m[0], m[1], m[2], m[3], m[4], m[5])
ctx.fillStyle = 'blue'
ctx.fillRect(100, 100, 250, 100)