默认情况下,一个画布的坐标空间会使用画布的左上角 (0,0) 作为原点,x 值向右增加,y 值向下增加。这个坐标空间中的单位通常会被转换为像素,然后,可通过转换坐标空间在绘图过程中实现移动、缩放或旋转等操作。这些操作是通过 translate()、scale() 和 rotate()等方法来实现的,它们会对画布的变换矩阵产生影响。3.1.1 放大与缩小我们用scale函数来实现图形的放大和缩小。其函数原型如下:
scale(x, y);其中,第一个参数x表示在x轴进行缩放,即水平缩放,第二个参数 y表示在y轴上进行缩放,即在竖直方向上进行缩放。具体实现方法如代码清单3-1所示。代码清单 3-1
<script type="text/javascript"> var c=document.getElementById("myCanvas"); var ctx=c.getContext("2d"); ctx.beginPath(); ctx.strokeStyle = "#000000"; ctx.strokeRect(10,10,150,100); ctx.scale(3,3); ctx.beginPath(); ctx.strokeStyle = "#cccccc"; ctx.strokeRect(10,10,150,100); </script>运行后的效果如图3-1所示。下面解释一下代码清单3-1的代码。首先,以页面上的坐标(10,10)为起点开始画了一个宽150、高100的黑色矩形,如下所示:
ctx.beginPath(); ctx.strokeStyle = "#000000"; ctx.strokeRect(10,10,150,100);然后,沿着x轴和y轴方向将其值放大3倍,并用同样的代码重新画一个灰色的矩形,如下所示:
ctx.scale(3,3); ctx.beginPath(); ctx.strokeStyle = "#cccccc"; ctx.strokeRect(10,10,150,100);从运行效果图可以看到,虽然用了同样的代码,但是第二次画的灰色矩形被放大了3倍,并且起始坐标值也一起被放大了3倍。上述代码清单3-1实现了放大功能,那么现在来看看缩小功能如何实现,如代码清单3-2所示。代码清单 3-2
<script type="text/javascript"> var c=document.getElementById("myCanvas"); var ctx=c.getContext("2d"); ctx.beginPath(); ctx.strokeStyle = "#000000"; ctx.strokeRect(50,50,150,100); ctx.scale(0.5,0.5); ctx.beginPath(); ctx.strokeStyle = "#cccccc"; ctx.strokeRect(50,50,150,100); </script>运行后的效果如图3-2所示。
在代码清单3-2中,同样先画出了一个黑色矩形,然后沿着x轴和y轴方向上分别将其值缩小到了0.5倍,并用同样的代码重新画了一个灰色的矩形。从运行效果图可以看到,虽然用了同样的代码,但是第二次画的灰色矩形被缩小为原来的0.5倍了,并且起始坐标值也一起缩小了0.5倍。另外还需要了解的是,使用scale函数时,如果将参数设置为负数,还可以使图形翻转,比如代码清单3-3就实现了竖直方向的翻转。代码清单 3-3
<!DOCTYPE HTML> <html> <head> <meta charset="utf-8" /> </head> <body> <canvas id="myCanvas" width="500" height="350" style="background-color: #cccccc;"> 你的浏览器不支持HTML5 </canvas> <script type="text/javascript"> var c=document.getElementById("myCanvas"); var ctx=c.getContext("2d"); var image = new Image(); image.src = "face.jpg"; image.onload = function(){ ctx.drawImage(image,10,10); ctx.scale(1,-1); ctx.drawImage(image,250,-250); }; </script> </body> </html>运行效果如图3-3所示。
3.1.2 平移使用translate函数可以实现对图形进行平移的功能。其函数原型如下:
translate (x, y);其中,第一个参数x表示在x轴进行平移,即在水平方向上平移,第二个参数 y表示在y轴上进行缩放,即在竖直方向上平移。具体的实现方法如代码清单3-4所示。代码清单 3-4
<script type="text/javascript"> var c=document.getElementById("myCanvas"); var ctx=c.getContext("2d"); ctx.beginPath(); ctx.strokeStyle = "#000000"; ctx.strokeRect(10,10,150,100); ctx.translate(50,100); ctx.beginPath(); ctx.strokeStyle = "#cccccc"; ctx.strokeRect(10,10,150,100); </script>运行效果如图3-4所示。
在代码清单3-4中的如下代码表示在水平方向上向右平移50,在竖直方向上向下平移100,图3-4中灰色矩形是平移之后的效果。
ctx.translate(50,100);3.1.3 旋转利用rotate函数可以实现图形的旋转功能。其函数原型如下:
rotate (angle);这里需要注意的是,传入rotate里的参数angle是弧度而不是角度。如果角度为angle,那么换算为弧度就是angle*Math.PI/180,具体实现方法如代码清单3-5所示。代码清单 3-5
<script type="text/javascript"> var c=document.getElementById("myCanvas"); var ctx=c.getContext("2d"); ctx.beginPath(); ctx.strokeStyle = "#000000"; ctx.strokeRect(200,50,100,50); ctx.rotate(45*Math.PI/180); ctx.beginPath(); ctx.strokeStyle = "#ff0000"; ctx.strokeRect(200,50,100,50); </script>运行效果如图3-5所示。在图3-5中灰色矩形是旋转45度后的图形,可以看到用rotate来实现旋转时,是以Canvas的起始坐标(0,0)为中心进行旋转的。如果要想让图形以自己为中心旋转,则需要使用3.1.2节中的translate函数,如代码清单3-6所示。
代码清单 3-6
<script type="text/javascript"> var c=document.getElementById("myCanvas"); var ctx=c.getContext("2d"); ctx.beginPath(); ctx.strokeStyle = "#000000"; ctx.strokeRect(200,50,100,50); ctx.translate(250,75); ctx.rotate(45*Math.PI/180); ctx.translate(-250, -75); ctx.beginPath(); ctx.strokeStyle = "#ff0000"; ctx.strokeRect(200,50,100,50); </script>运行效果如图3-6所示。
在代码清单3-6中,下面的代码表示先将Canvas的起始坐标向右移250,向下移75,即移至所画矩形的中心处;然后开始旋转45度;接着再将Canvas的起始坐标向左移250,向上移75,即移回到原来位置,这样就完成了图形以自己为中心的旋转。
ctx.translate(250,75); ctx.rotate(45*Math.PI/180); ctx.translate(-250, -75);3.1.4 利用transform矩阵实现多样化的变形上面分别讲了缩放、平移以及旋转的实现方法。其实所有这些变形都是可以通过变形矩阵transform来实现的。先来看看transform函数的定义。transform函数的原型如下:
transform (a,b,c,d,e,f);该函数的各个变量对应以下变换矩阵中相应位置的参数。a c eb d f0 0 1下面来说明如何使用这个变换矩阵来实现上面的各种变形。1 . 缩放假设原始坐标为(x,y),缩放后的坐标为(x1,y1),缩放的倍数分别为a和d,那么就有下列公式:
x1=a*x y1=d*y因此,我们得到了如下矩阵公式。
x1 a 0 0 x y1 = 0 d 0 y 1 0 0 1 1这样就可以用transform (a,0,0,d,0,0)来替换scale(a,d)了,如代码清单3-7所示。代码清单 3-7
<script type="text/javascript"> var c=document.getElementById("myCanvas"); var ctx=c.getContext("2d"); ctx.beginPath(); ctx.strokeStyle = "#000000"; ctx.strokeRect(10,10,150,100); ctx.transform(3,0,0,3,0,0); ctx.beginPath(); ctx.strokeStyle = "#cccccc"; ctx.strokeRect(10,10,150,100); </script>运行效果如图3-7所示。
可以看到,这里用代码清单3-7实现了和代码清单3-1一样的功能。
平移同样,假设原始坐标为(x,y),平移后的坐标为(x1,y1),在x轴和y轴的平移量分别为e和f,那么就有下列公式: x1=x+e y1=y+f因此,我们得到如下矩阵公式。
x1 1 0 e x y1 = 0 1 f y 1 0 0 1 1这样就可以用transform (1,0,0,1,e,f)来替换translate (e,f)了,如代码清单3-8所示。代码清单 3-8
<script type="text/javascript"> var c=document.getElementById("myCanvas"); var ctx=c.getContext("2d"); ctx.beginPath(); ctx.strokeStyle = "#000000"; ctx.strokeRect(10,10,150,100); ctx.transform (1,0,0,1,50,100); ctx.beginPath(); ctx.strokeStyle = "#cccccc"; ctx.strokeRect(10,10,150,100); </script>运行效果如图3-8所示。
可以看到,这里用代码清单3-8实现了和代码清单3-4一样的功能。3 . 旋转旋转对应的矩阵公式如下。
x1 cos –sin 0 x y1 = sin cos 0 y 1 0 0 1 1现在可以用transform (cos , sin , –sin , cos ,0,0)来替换rotate ()了,如代码清单3-9所示。代码清单 3-9
<script type="text/javascript"> var c=document.getElementById("myCanvas"); var ctx=c.getContext("2d"); ctx.beginPath(); ctx.strokeStyle = "#000000"; ctx.strokeRect(200,50,100,50); ctx.transform (Math.cos(45*Math.PI/180), Math.sin(45*Math.PI/180), -Math.sin(45*Math.PI/180), Math.cos(45*Math.PI/180),0,0); ctx.beginPath(); ctx.strokeStyle = "#cccccc"; ctx.strokeRect(200,50,100,50); </script> </body>运行效果如图3-9所示。可以看到,这里用代码清单3-9实现了和代码清单3-5一样的功能。这样,我们就用transform完成了所有变形。另外,变换矩阵也可以通过setTransform函数来实现,setTransform的参数与transform一样,不同的是,setTransform函数是先消去之前的transform变换,然后重新进行变换的。为了区分transform和setTransform,我们看看下面的例子,如代码清单3-10所示。
代码清单 3-10
<script type="text/javascript"> var c=document.getElementById("myCanvas"); var ctx=c.getContext("2d"); ctx.beginPath(); ctx.strokeStyle = "#000000"; ctx.strokeRect(200,50,100,50); ctx.transform (Math.cos(5*Math.PI/180), Math.sin(5*Math.PI/180), -Math.sin(5*Math.PI/180), Math.cos(5*Math.PI/180),0,0); ctx.beginPath(); ctx.strokeStyle = "#cccccc"; ctx.strokeRect(200,50,100,50); ctx.transform (Math.cos(10*Math.PI/180), Math.sin(10*Math.PI/180), -Math.sin(10*Math.PI/180), Math.cos(10*Math.PI/180),0,0); ctx.beginPath(); ctx.strokeStyle = "#999999"; ctx.strokeRect(200,50,100,50); </script>运行效果如图3-10所示。
在代码清单3-10中,首先旋转5度,然后再旋转10度,所以第二次旋转相当于旋转了15度。下面再看看代码清单3-11中setTransform的使用方法。代码清单 3-11
<script type="text/javascript"> var c=document.getElementById("myCanvas"); var ctx=c.getContext("2d"); ctx.beginPath(); ctx.strokeStyle = "#000000"; ctx.strokeRect(200,50,100,50); ctx.transform (Math.cos(5*Math.PI/180), Math.sin(5*Math.PI/180), -Math.sin(5*Math.PI/180), Math.cos(5*Math.PI/180),0,0); ctx.beginPath(); ctx.strokeStyle = "#cccccc"; ctx.strokeRect(200,50,100,50); ctx.setTransform (Math.cos(10*Math.PI/180), Math.sin(10*Math.PI/180), -Math.sin(10*Math.PI/180), Math.cos(10*Math.PI/180),0,0); ctx.beginPath(); ctx.strokeStyle = "#999999"; ctx.strokeRect(200,50,100,50); </script>运行效果如图3-11所示。可以看到,第二次的旋转没有在第一次旋转的基础上进行,因为用setTransform实现旋转效果的时候,已经清除了上一个transform,所以只旋转了10度。4 . 倾斜接着来看一下如何实现图像的倾斜效果。首先看图3-12。
图中的3个点p0、p1、p2遵循以下矩形公式。
(p1.x–p0.x)/width (p2.x–p0.x)/height p0.x (p1.y–p0.y)/width (p2.y–p0.y)/height p0.y 0 0 1将上面的矩形公式带入setTransform函数中,如代码清单3-12所示。代码清单 3-12
<script type="text/javascript"> var c=document.getElementById('myCanvas'); var ctx=c.getContext('2d'); ctx.setTransform(1,10/150,-40/100,1,40,10); ctx.rect(50,50,150,100); ctx.stroke(); </script>运行效果如图3-13所示。上述倾斜效果也可以根据另外3个点来实现,看图3-14。
图中的3个点p1、p2、p3遵循以下矩形公式。
(p3.x–p2.x)/width (p3.x–p1.x)/height p2.x (p3.y–p2.y)/width (p3.y–p1.y)/height p2.y 0 0 1将上面的矩形公式代入setTransform函数中,如代码清单3-13所示。代码清单 3-13
<script type="text/javascript"> var c=document.getElementById('myCanvas'); var ctx=c.getContext('2d'); ctx.setTransform(130/150,-20/150,-20/100,80/100,0,0); ctx.rect(50,50,150,100); ctx.stroke(); </script>运行效果如图3-15所示。
5 . 图片的扭曲效果根据以上所介绍的内容,已经可以做到倾斜、旋转、平移和缩放4种基本变形,如图3-16所示。
但是,对于非上述4种基本变形的特殊变形,就无法直接实现了。比如图3-17中的扭曲变形效果。这时候,就需要通过多种变形组合来实现。先对图3-17进行分解,如图3-18所示。此时分解出的两个图形可分别看作是两个基本变形的一部分,如图3-19所示。
这样一来就简单了,只需要实现两个倾斜的变形,然后利用clip函数将图片的一部分绘制出来,就可以完成图3-17中的效果了,如代码清单3-14所示。代码清单 3-14
<script type="text/javascript"> var c=document.getElementById('myCanvas'); var ctx=c.getContext('2d'); var img = new Image(); img.src="face.jpg"; img.onload = function(){ ctx.save(); ctx.beginPath(); ctx.moveTo(80,0); ctx.lineTo(320,40); ctx.lineTo(0,200); ctx.closePath(); ctx.clip(); ctx.setTransform((320-80)/240,40/240,-80/240,200/240,80,0); ctx.drawImage(img,0,0); ctx.restore(); ctx.save(); ctx.beginPath(); ctx.moveTo(320,40); ctx.lineTo(0,200); ctx.lineTo(200,150); ctx.closePath(); ctx.clip(); ctx.setTransform(200/240,(150-200)/240,(200-320)/240,(150-40)/240,0,200); ctx.drawImage(img,0,0-240); ctx.restore(); }; </script>运行上面的代码,就可以得到图3-17的效果了。解释一下代码清单3-14。首先看一下左半边图形的绘制。
ctx.beginPath(); ctx.moveTo(80,0); ctx.lineTo(320,40); ctx.lineTo(0,200); ctx.closePath(); ctx.clip();上面这段代码是以(80,0)、(320,40)和(0,200) 3个点为顶点绘制一个三角形,然后利用clip函数将这个三角形作为绘图的可视区域。
ctx.setTransform(200/240,(150-200)/240,(200-320)/240,(150-40)/240,0,200);上面的代码是以刚才的三角形的3个顶点来进行倾斜变形的,这个变形的原理已经在代码清单3-12中讲过了。
ctx.drawImage(img,0,0);上面的代码是绘制图片,因为绘图的可视区域只是一个三角形,所以绘制完的图片只有一部分。右半边图形的绘制思路和左半边是一样的。同时,两次绘图都加上了save()函数和restore()函数,这是为了让两次变形和绘图互不干涉。
相关资源:从入门到精通HTML5——PDF——网盘链接