Skip to content

变形操作

状态的恢复和保存

  1. 状态的保存 :save() ,保存画布的所有状态
  2. 状态的恢复:restore(),恢复画布状态

画布中的状态在每次调用sava()方法之后,保存到状态栈中,每次restore(),状态栈中最后的状态会弹出,并恢复所有设置。

没有save() 时:

typescript
//这里设置一些初始样式 填充颜色为红色,并绘制一个矩形
canvas.beginPath()
canvas.fillStyle = `red`;
canvas.rect(0,0,100,100)
canvas.fill()
canvas.closePath()//这里使用闭合路径是更明确第一个矩形的绘制已结束

//这里没有保存上一个矩形的状态,并再添加一个矩形
canvas.rect(200,0,100,100);
//填充矩形之后会沿用上一个矩形的样式设置 canvas.fillStyle = `red`;
canvas.fill()

效果图:

canvas-save-example-1

调用save() 时:

typescript
const colors = ["red", "#8e44ad", "#f1c40f"];

for (let i = 0; i < colors.length; i++) {
    //重新开始一个新的路径
    canvas.beginPath()
    //设置不同样式
    canvas.fillStyle = colors[i];
    canvas.rect(i * 100 * 2, 0, 100, 100)
    canvas.fill()
    canvas.closePath()
    //保存每次的状态
    canvas.save()
}

效果图:

canvas-save-example-2

恢复canvas状态restore()

typescript
for (let i = 0; i < colors.length; i++) {
    //重新开始一个新的路径
    canvas.beginPath()
    //设置不同样式
    canvas.fillStyle = colors[i];
    canvas.rect(i * 100 * 2, 0, 100, 100)
    canvas.fill()
    canvas.closePath()
    //保存每次的状态
    canvas.save()
}

//再开启一个循环
for (let i = 0; i < colors.length; i++) {
    //恢复状态栈中的最顶部的状态
    canvas.restore()
    //重新开始一个新的路径
    canvas.beginPath()
    //不设置样式,会使用上一个保存的样式
    // canvas.fillStyle = colors[i];
    canvas.rect(i * 100 * 2, 200, 100, 100)
    canvas.fill()
    canvas.closePath()
}

效果图:

canvas-restore-example-1

移动

如果不使用 translate 方法,那么所有矩形都将被绘制在相同的位置(0,0)。translate 方法同时让我们可以任意放置这些图案,而不需要在 fillRect() 方法中手工调整坐标值,既好理解也方便使用。

typescript
//绘制九宫格
for (var i = 0; i < 3; i++) {
    for (var j = 0; j < 3; j++) {
        //保存默认设置
        canvas.save();
        canvas.strokeStyle = "#3498db"
        //移动画布,设置每次绘制时的x,y轴上的偏移量
        canvas.translate(10 + j * 50, 10 + i * 50);
        //绘制矩形,不需要设置矩形的x,y
        canvas.strokeRect(0, 0, 25, 25);
        //恢复默认设置,将原点重新移动到(0,0),将画布的移动设置为默认值,不然当循环足够多时,会有些图形超出画布范围
        canvas.restore();
    }
}

效果图:

canvas-translate-example-1

旋转

改变变形原点,旋转的中心点始终是 canvas 的原点(0,0),如果要改变它,我们需要用到 translate方法。

typescript
//绘制画布的线框
canvas.strokeStyle = 'rgba(0,0,0,0.4)'
canvas.strokeRect(0, 0, 1000, 800)


const colors = ["#1abc9c", "#9b59b6", "#34495e", "#f39c12", "#c0392b", "#2980b9"]

//改变变形原点,这里的画布大小是1000,800
canvas.translate(500, 400)

//循环数组
for (let i = 0; i < colors.length; i++) {
    canvas.save()
    canvas.fillStyle = colors[i];
    canvas.fillRect(0,0,100,100);
    //一个循环6次,每次60度
    canvas.rotate((Math.PI / 180) * 60)
    // canvas.restore()//这里不调用restore,不然画布的角度会被重制
}

效果图:

canvas-rotate-example-1

变形

可以通过transform(a,b,c,d,e,f)方法设置对画布的倾斜、缩放、平移。

参数讲解:

  1. a:这是变换矩阵的第一行第一列元素,表示 x 轴方向的缩放(scaling)。如果 a 是 1,表示不对 x 轴进行缩放;如果 a 大于 1,表示在 x 轴方向上进行放大;如果 a 小于 1,表示在 x 轴方向上进行缩小。
  2. b:这是变换矩阵的第二行第一列元素,表示 x 轴方向的倾斜(skewing)。它不会影响 x 轴的方向,但会在 x 轴方向上产生倾斜效果。
  3. c:这是变换矩阵的第一行第二列元素,表示 y 轴方向的倾斜(skewing)。它不会影响 y 轴的方向,但会在 y 轴方向上产生倾斜效果。
  4. d:这是变换矩阵的第二行第二列元素,表示 y 轴方向的缩放(scaling)。如果 d 是 1,表示不对 y 轴进行缩放;如果 d 大于 1,表示在 y 轴方向上进行放大;如果 d 小于 1,表示在 y 轴方向上进行缩小。
  5. e:这是变换矩阵的第一行第三列元素,表示 x 轴方向的平移(translation)。它将整个坐标系沿着 x 轴平移指定的距离。
  6. f:这是变换矩阵的第二行第三列元素,表示 y 轴方向的平移(translation)。它将整个坐标系沿着 y 轴平移指定的距离。
typescript
// 开始绘制路径
canvas.beginPath();
canvas.rect(50, 50, 100, 100); // 在 (50, 50) 处创建一个宽高为 100 的正方形
// 设置填充颜色
canvas.fillStyle = "#e74c3c";
canvas.fill();

// 应用变换 移动画布到(50,50) 只移动,不缩放,不倾斜
// 倾斜45度 平移x=50,y=50,不缩放,y轴倾斜 45度
canvas.transform(1, 0, Math.sin(Math.PI / 4), 1, 50, 50);

// 重新绘制路径
canvas.beginPath();
canvas.rect(0, 0, 100, 100); // 在 (0, 0) 处创建一个宽高为 100 的正方形
// 设置填充颜色
canvas.fillStyle = "#2ecc71";
canvas.fill();

效果图:

canvas-transform-example-1

调整代码,就实现旋转的效果:

typescript
canvas.transform(1, - Math.sin(Math.PI / 4), Math.sin(Math.PI / 4), 1, 50, 50);

效果如下:

canvas-transform-example-2