简介:
在上一篇博客中,我们介绍了canvas对图片的操作,在这一里我们将继续介绍canvas的变换方法,这些方法很类似于CSS,如果你对CSS比较了解,那么这些方法将十分轻松:旋转rotate,缩放scale,移动translate,矩阵变换transform,下面我们将介绍这些方法:
1.状态的保存和恢复save() 和restore()
状态
绘制复杂图形时涉及到状态的保存和恢复,在canvas中一个绘画状态是指:
- 当前应用的变形(移动,旋转和缩放)
- 填充,轮廓和线型:strokeStyle, fillStyle, globalAlpha, lineWidth, lineCap, lineJoin, miterLimit,
- 阴影:shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor,
- globalCompositeOperation 的值
- 当前的裁切路径(clipping path)
Canvas 的状态就是当前画面应用的所有样式和变形的一个快照。
save保存和restore恢复
- save():保存画布(canvas)的所有状态
- restore():恢复画布的状态
注意:
(1)save 和 restore 方法是用来保存和恢复 canvas 状态的,都没有参数。
(2)Canvas状态存储在栈中,每当save()方法被调用后,当前的状态就被推送到栈中保存。
(3)调用 restore,状态栈中栈顶会弹出,并恢复至新的栈顶状态。
举例:
设置三种颜色,分别save和restore入栈退栈
function draw()
{
var canvas=document.getElementById("canvas");
if(canvas.getContext)
{
var ctx=canvas.getContext('2d');
var color=["green","blue","orange"];
for(var i=0;i<3;i++)
{
ctx.save();//保存当前状态入栈
ctx.fillStyle=color[i];
ctx.fillRect(20,50*i,200,50);
}
for(var i=0;i<3;i++)
{
ctx.restore();//弹出栈顶元素,使用下一个栈顶元素
ctx.fillRect(20,150+50*i,200,50);
}
}
}
translate(x,y)平移
- translate(x,y)方法接受两个参数。x 是左右偏移量,y 是上下偏移量,它用来移动 canvas 和它的原点到一个不同的位置,这个偏移量是相对于当前的原点来说的。
- 在做变形之前 先保存状态是一个良好的习惯。大多数情况下,调用 restore 方法比手动恢复原先的状态要简单得多。又,如果你是在一个循环中做位移但没有保存和恢复 canvas 的状态,很可能到最后会发现怎么有些东西不见了,那是因为它很可能已经超出 canvas 范围以外了。
- translate 方法同时让我们可以任意放置这些图案,而不需要在 fillRect() 方法中手工调整坐标值,既好理解也方便使用。
- translate方法要和CSS3中的translateX,translateY…区分开来
举例:
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
for(var i=0;i<4;i++)
{
for(var j=0;j<3;j++)
{
ctx.save();//保存
ctx.fillStyle="rgb("+50*i+",255,0)";
ctx.translate(10+i*50,10+j*50);
ctx.fillRect(0,0,25,25);
ctx.restore();//清除
}
}
}
translate 方法同时让我们可以任意放置这些图案,而不需要在 fillRect() 方法中手工调整坐标值,既好理解也方便使用。
3.旋转rotate(deg)
- 旋转rotate(deg)方法: 我们可以给绘制好的路径图形设置旋转,默认是顺时针方向,单位是弧度Math.PI/180,旋转的相对中心是当前canvas的原点,我们可以用translate方法来设置原点.
举例:
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
ctx.translate(100,100);
for(var i=0;i<5;i++)
{
ctx.save();
ctx.fillStyle="rgb("+51*i+","+(255-51*i)+",255)";
for(var j=0;j<6*i+1;j++)
{
ctx.rotate(Math.PI/(3*i));
ctx.beginPath();
ctx.arc(0,12.5*i,5,0,Math.PI*2,true);
ctx.fill();
}
ctx.restore();//清除
}
}
3.缩放 Scale(x,y)
-
缩放scale(x,y) :给图形设置横向和纵向的缩放,我们用它来增减图形在 canvas 中的像素数目,对形状,位图进行缩小或者放大。
-
scale 方法可以缩放画布的水平和垂直的单位。两个参数都是实数,可以为负数,x 为水平缩放因子,y 为垂直缩放因子,如果比1小,会比缩放图形, 如果比1大会放大图形。默认值为1, 为实际大小。
-
镜像翻转:如果参数为负实数, 相当于以x 或 y轴作为对称轴镜像反转(例如, 使用translate(0,canvas.height); scale(1,-1); 以y轴作为对称轴镜像反转, 就可得到著名的笛卡尔坐标系,左下角为原点)。
-
注意:放缩是相对于现有原点坐标的线性等比例放大或缩小,比如说如下的图形将是完全相等的
ctx.fillRect(10, 30, 100, 30);//未放缩
ctx.scale(10, 3);
ctx.fillRect(1, 10, 10, 10);
举例:
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
// draw a simple rectangle, but scale it.
ctx.save();
ctx.fillStyle="rgba(51,200,255,0.2)";
ctx.scale(10, 3);
ctx.fillRect(1, 10, 10, 10);
ctx.restore();
// mirror horizontally
ctx.font = '48px serif';
ctx.fillText('Hello', 10, 120);
ctx.scale(-1, 1);
ctx.fillText('Hello',-135, 180);
}
4.矩阵变换transform:
transform方法
实际上上面提供的rotate旋转,scale放缩以及translate平移都能用一个变形矩阵来进行变换。只要我们操纵这个变形矩阵,就能进行变换,transform方法提供了这样的操作方法。将当前的变形矩阵乘上一个基于自身参数的矩阵,在这里我们用下面的矩阵描述:
m11 m21 dx
m12 m22 dy
0 0 1
如果任意一个参数是无限大,变形矩阵也必须被标记为无限大,否则会抛出异常。
参数如下:
- m11:水平方向的缩放
- m12:水平方向的倾斜偏移
- m21:竖直方向的倾斜偏移
- m22:竖直方向的缩放
- dx:水平方向的移动
- dy:竖直方向的移动
setTransform(m11, m12, m21, m22, dx, dy)方法
将当前的变形矩阵重置为单位矩阵,然后用相同的参数调用 transform 方法。如果任意一个参数是无限大,那么变形矩阵也必须被标记为无限大,否则会抛出异常。从根本上来说,该方法是取消了当前变形,然后设置为指定的变形,一步完成。
resetTransform()方法
重置当前矩阵为单位矩阵,等价于:
ctx.setTransform
ctx.setTransform(1,0,0,1,0,0);
举例:
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);
}