很多时候,我们绘制出一个图形之后,并不能达到我们预期的效果,这个时候,适当地运用图形的变换(transformations,如旋转和缩放等),可以创建出大量复杂多变的图形。
1、保存和恢复Canvas状态
Canvas指的是当前画面的所有样式、变形和裁切的一个快照,以堆的方式保存。save和restore方法用于保存和恢复Canvas状态,这两个方法都不需要任何参数,用法如下:
context.save();
context.restore();
sava方法可以暂时将当前状态保存在堆中,这些状态可以是各种属性(如strokeStyle、fillStyle和globalCompositeOperation等)的值。当前应用的变形、当前裁切的路径等。restore方法用于将上一个保存状态从堆中再次取出,恢复该状态的所有设置。
举一个简单的示例:
先来看看最后的效果图:
我们保存的Canvas状态属性值是fillStyle填充为红色,strokeStyle轮廓为蓝色,当我们没有调用restore方法恢复时,依旧使用的是我们自己定义的属性值。但是,我们可以看到当我们调用restore后,绘制出来的矩形填充以及轮廓就是我们之前保存的值。
2、移动坐标空间
在上一篇Canvas元素中我们提到过,画布的坐标空间默认的是以画布左上角(0,0)为原点,x轴水平向右为正向,y轴垂直向下为正向。而在绘制图形时,我们可以使用translate方法移动坐标空间,使画布的变换矩阵发生水平和垂直方向的偏移,其用法如下:
context.translate(dx,dy);
其中dx和dy分别为坐标原点沿水平和垂直两个方向的偏移量。如下图所示:
当然了,在进行图形变换之前,最好先要养成使用save方法保存当前状态的好习惯。在许多情况下,使用restore方法来自动恢复原来的状态要比手动恢复更高效,特别是当重复某种运算时。接下来,我们就通过绘制一个伞状图形的示例加深对它的理解。
最后的效果图:
在这里,我只想说的一点是,关于
ctx.fillRect(-1.5,0,1.5,40);和ctx.arc(-5,40,4,Math.PI,Math.PI*2,true);
这两个语句的坐标问题,主要是取决于你后面绘制的图形的宽度。
Canvas中图形移动的实现,实际上是通过改变画布的坐标原点实现的,所谓的“移动图形”,只是看上去被移动的样子,移动的是其实是坐标空间。
3、旋转坐标空间
rotate方法用于以原点为中心旋转Canvas,实质上旋转的是Canvas上下文对象的坐标空间,具体用法是:
context.rotate(angle);
rotate方法只有一个参数,即旋转角度angle,其顺时针方向为正方向,以弧度为单位,旋转中心是Canvas的原点。
拿上一个伞状例子来说,我现在的状态是一行排列的,那么我现在想要将它旋转成一个圆形队列,又该怎么实现呢?
function draw(){
var ctx = document.getElementById("myCanvas").getContext("2d");
// 垂直方向和水平方向发生了偏移
ctx.translate(150,150);
for(var i=1;i<9;i++){
ctx.save();
// 图片旋转角度
ctx.rotate(Math.PI*(2/4+i/4));
// 水平方向发生偏移
ctx.translate(0,-100);
// 绘制伞状图形的上半部分,也就是半圆部分,第二个参数表示填充颜色
drawTop(ctx,"rgb("+(30*i)+","+(255-30*i)+",255)");
// 绘制伞状图形的下半部分
drawGrip(ctx);
ctx.restore();
}
}
其实,到最后,我们会发现,只是draw方法发生了变化。具体是什么变化呢?无非就是多了ctx.rotate(Math.PI*(2/4+i/4));和ctx.translate(0,-100);语句。那么接下来,我们一起思考为什么要使用这两个方法里的数据。
首先,我们首先明确Math.PI是180度,那么很显然旋转一圈是360度,也就是说,后面的参数(2/4+i/4)中,i=1时表示初始旋转的位置,后面的则依次递加。
其次,为什么每次都要将坐标空间沿y轴负方向移动100px呢?那是因为,如果我们不移动,最后所有的图形都会重合成一个圆,就像下面这样:
而我们实际想要的效果是什么呢?
4、缩放图形
scale方法用于增减Canvas上下文对象中的像素数目,从而实现图形或位图的放大或缩小,其用法是:
context.scale(x,y);
其中x,y为必须接受的参数,x为横轴的缩放因子,y轴为纵轴的缩放因子,它们的值必须是正值。
若放大图形,参数值需大于1;
若缩小图形,参数值需小于1;
若参数值等于1,无任何效果。
我们可以用一个螺旋状由大到小的例子来深入理解缩放的使用:
其实,这也不难理解,我们反反复复使用的都是提到过的方法,唯一的不同是,我们现在把所有的方法整合在了一起,我们传递的参数不再是一个确定的值,所以,我们实现的效果也是千变万化的。
关于Canvas元素其实还有其他的很多方法,后续将会继续补充,希望大家能够共同进步!