canvas--变形

1.状态的保存和回复 Saving and Restoring state

1.1 save()

保存画布(canvas)当前的所有状态

1.2 restore()

恢复画布(canvas)的上一个已保存的状

Canvas状态存储在栈中,每当save()方法被调用后,当前的状态就被推送到栈中保存。

一个绘画状态包括:

  • 当前应用的变形(即移动,旋转和缩放)
  • strokeStyle, fillStyle, globalAlpha, lineWidth, lineCap, lineJoin, miterLimit, shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor, globalCompositeOperation 的值
  • 当前的裁切路径(clipping path)
    可以调用任意多次 save 方法。
每一次调用 restore 方法,上一个保存的状态就从栈中弹出,所有设定都恢复。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function draw() {
var canvas = document.getElementById('tutorial');
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
ctx.fillRect(0, 0, 150, 150); // 默认 #000
ctx.save();
ctx.fillStyle = "#f00";
ctx.fillRect(20, 20, 110, 110);
ctx.save();
ctx.fillStyle = "#ff0";
ctx.fillRect(40, 40, 70, 70);
ctx.save();
ctx.restore();
ctx.fillRect(30, 30, 90, 90); // #ff0
ctx.restore();
ctx.fillRect(10, 10, 130, 130); // #f00
ctx.restore();
ctx.fillRect(0, 0, 150, 150); // #000

}
}

2.移动 Translating

1.1 translate(x, y)它用来移动 canvas 和它的原点到一个不同的位置。

translate 方法接受两个参数。

  • x 是左右偏移量
  • y 是上下偏移量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function draw() {
var canvas = document.getElementById('tutorial');
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
ctx.save();
ctx.fillStyle = 'rgb(' + (51 * i) + ',' + (255 - 51 * i) + ',255)';
ctx.translate(10 + j * 50, 10 + i * 50);
ctx.fillRect(0, 0, 25, 25)
ctx.restore();

}

}

}
}

未使用translate,如果不使用 translate 方法,那么所有矩形都将被绘制在相同的位置(0,0)

使用translate,可以移动 canvas 原点,translate 方法同时让我们可以任意放置这些图案,而不需要在 fillRect() 方法中手工调整坐标值

2.旋转 Rotating

2.1 rotate(angle) 以原点为中心旋转 canvas

rotate(angle)
这个方法只接受一个参数:旋转的角度(angle),它是顺时针方向的,以弧度为单位的值。
旋转的中心点始终是 canvas 的原点,如果要改变它,我们需要用到 translate 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function draw() {
var canvas = document.getElementById('tutorial');
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
ctx.translate(75, 75); // 每次循环之后,圆心的位置都回到(75,75)
for (let i = 0; i < 6; i++) { // 第一层循环决定环的数量
ctx.save();
ctx.fillStyle = 'rgb(' + (51 * i) + ',' + (255 - 51 * i) + ',255)';
for (let j = 0; j < i*1; j++) { // 第二层循环决定每环有多少个点
// 每次画圆点,我都以一定夹角来旋转 canvas,而这个夹角则是由环上的圆点数目的决定的。最里层的环有 6 个圆点,这样,每次旋转的夹角就是 360/6 = 60 度。
// 往外每一环的圆点数目是里面一环的 2 倍,那么每次旋转的夹角随之减半。
ctx.rotate(Math.PI * 2 / (i *
6)) // 第一圈旋转0° 第二圈旋转60° 第三圈旋转30° 第四圈旋转15° 第五圈旋转12° 第六圈旋转10° 第七圈旋转8.57°...
ctx.beginPath();
ctx.arc(0, i * 12.5, 5, 0,Math.PI * 2, true); // x,y,r,startAngle,endAngle,anticlockwise
ctx.fill();
}
ctx.restore();

}

}
}

j < i*1;
当i分别为0,1,2,3,4,5时,效果如下:

3.缩放 Scaling

3.1 scale(x,y) 增减图形在 canvas 中的像素数目,对形状,位图进行缩小或者放大

两个参数都是实数,可以为负数,如果比1小,会比缩放图形, 如果比1大会放大图形。默认值为1, 为实际大小。

  • x 为水平缩放因子
  • y 为垂直缩放因子
1
2
3
4
5
6
7
8
9
10
function draw() {
var canvas = document.getElementById('tutorial');
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
ctx.save();
ctx.scale(10, 3);
ctx.fillRect(1, 10, 10, 10);
ctx.restore();
}
}

未设置缩放

设置缩放 ctx.scale(10, 3);

1
2
3
4
5
6
7
8
9
function draw() {
var canvas = document.getElementById('tutorial');
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
ctx.scale(-1, 1);
ctx.font = '48px serif';
ctx.fillText('Nancy', -135, 120)
}
}

4.变形 Transforms

4.1 transform(m11, m12, m21, m22, dx, dy) 将当前的变形矩阵乘上一个基于自身参数的矩阵

  • m11:水平方向的缩放
  • m12:水平方向的倾斜偏移
  • m21:竖直方向的倾斜偏移
  • m22:竖直方向的缩放
  • dx:水平方向的移动
  • dy:竖直方向的移动

    4.2 resetTransform() 重置当前变形为单位矩阵

    等同于
    1
    ctx.setTransform(1, 0, 0, 1, 0, 0);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');

var sin = Math.sin(Math.PI/6);
var cos = Math.cos(Math.PI/6);
ctx.translate(100, 100);
var c = 0;
for (var i=0; i <= 12; i++) {
c = Math.floor(255 / 12 * i);
ctx.fillStyle = "rgb(" + c + "," + c + "," + c + ")";
ctx.fillRect(0, 0, 100, 10);
ctx.transform(cos, sin, -sin, cos, 0, 0);
}

ctx.setTransform(-1, 0, 0, 1, 100, 100);
ctx.fillStyle = "rgba(255, 128, 255, 0.5)";
ctx.fillRect(0, 50, 100, 100);
}