IT技术博客大学习 共学习 共进步

Canvas高级特性

RockUX | WEB、前端、JavaScript、PHP 2011-03-27 23:55:35 浏览 2,964 次

在我们的一系列文章中,我们上一次讲了Canvas元素,还有一些基本的操作。这篇文章中,我们再来介绍一些Canvas高级的方法。

准备工作

我们跟上一次用同样的一个HTML模板,所以打开你喜欢的编辑器,然后敲入下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>  
 
<html>
    <head>
        <title>Canvas from scratch</title>
        <meta charset="utf-8">  
 
        <script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>  
 
         <script>
             $(document).ready(function() {
                 var canvas = document.getElementById("myCanvas");
                 var ctx = canvas.getContext("2d");
             });
         </script>
     </head>  
 
     <body>
         <canvas id="myCanvas" width="500" height="500">
             <!-- Insert fallback content here -->
         </canvas>
     </body>
</html>

这段代码没什么特别的,就是一个Canvas元素,然后js代码会在元素加载完毕之后执行。

如何画一个圆

上一篇文章中,我们讲到了如何画一些基本的图形和路径,这里我们会深入一些,讲讲如何画圆。可能比你想象的要复杂一些,不过也不是多么的困难。

没有可以让你通过一句话就可以画出一个圆的代码,就像使用fillRect画一个矩形一样。你需要使用画path的方法,然后调用arc函数,圆就是一个360度的弧。这样做的原因是,圆实际上是一个比较复杂的图形,使用arc方法你可以做各种的操作,例如,你只想画一个半圆。你甚至也可以将arc方法和画直线的方法合并一起画一个四分之一圆或者扇形。

后面会介绍arc的原理,这里,我们先画一个圆:

1
2
3
4
context.beginPath();
context.arc(100,100,50,0,Math.PI*2,false);
context.closePath();
context.fill();

这段代码就会在画布上画出来一个圆。

看起来挺简单的,不过我们得仔细说说。

arc方法有六个参数:

  • 第一个是原点的x坐标
  • 第二个是原点的y坐标
  • 第三个是圆的半径
  • 第四个是起始的角度
  • 第五个是结束的角度
  • 第六个是画的方向(true是逆时针,false是顺时针)

写成伪代码的形式就是:

1
arc(x,y,radius,startAngel,endAngel,anticlockwise);

前三个参数不用多说,大家都明白,最后一个也是,那么起始角度和结束角度是什么呢?

之前有提到,圆就是一个360度的弧。在canvas中,弧定义为从起始点开始算起,定义半径长度内的一段曲线。这段曲线从一个定义的起始角度(第四个参数)开始,然后开始走,直到到了定义的结束的那个角度(第五个参数),听起来挺简单。

也许一个图会比较有帮助。

看起来还蛮复杂,不过相对于文字还是清晰了不少吧。

canvas中的角度

这个时候应该提一下,Canvas中的角度是用弧度定义的,而不是度数。这就意味着角度从0开始到2π结束。从右手边开始计算,可以看下图:

如果你不喜欢弧度,那么你也可以通过下面的方法将度数转为弧度:

1
2
var degrees = 270;
var radians = degrees * (Math.PI / 180);

这个公式就很简单啦。

贝塞尔曲线

弧线挺有意思的,不过在canvas里面他们的限制还比较大。如果你想画一些复杂的东西,那么可以使用贝塞尔曲线的方法quadraticCurveTo和bezierCurveTo。通过这些方法就可以画一些半径没有在中央的曲线以及那些由多个曲线组成的复杂的曲线path。

贝塞尔曲线通过控制点来决定如何画曲线。例如,quadraticCurveTo有一个控制点,bezierCurveTo有两个控制点,看看下面这个图,看看怎么通过这些控制点来画曲线。

如果你用过Adobe Illustrator这种通过矢量来画图的工具,你就可能对这两个曲线比较熟悉。

我们先看看怎么画贝塞尔曲线吧:

1
2
3
4
5
ctx.lineWidth = 8;
ctx.beginPath();
ctx.moveTo(50,150);
ctx.quadraticCurveTo(250,50,450,150);
ctx.stroke();

这就会画出来下面这样一条曲线。

quadraticCurveTo方法有四个参数:

  • 第一个是控制点的x坐标
  • 第二个是控制点的y坐标
  • 第三个参数是结束点的x坐标
  • 第四一个是结束点的y坐标

写成伪代码就是:

1
quadraticCurveTo(cpx,xpy,x,y);

曲线的开始位置就是当前的路径所在位置。例如,在上面的代码中,通过moveTo方法来确定开始位置。
我们在上面的基础上画一个贝塞尔曲线,看看代码:

1
2
3
4
5
ctx.lineWidth = 8;
ctx.beginPath();
ctx.moveTo(50,150);
ctx.bezierCurveTo(150,50,350,250,450,150);
ctx.stroke();

这段代码就会画出这样的图:

bezierCurveTo方法有六个参数:

  • 第一个参数是第一个控制点的x坐标
  • 第二个参数是第一个点的y坐标
  • 第三个参数是第二个控制点的x坐标
  • 第四个参数是第二个控制点的y坐标
  • 第五个参数是结束点的x坐标
  • 第六个参数是结束点的y坐标

写成伪代码就是:

1
bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y);

可能这样看着没什么,但是当组合了多个路径,或者嵌套了好几次之后,结果就炫了。你可以用这些方法在Canvas上画出任何疯狂的东西出来。

你可能想要一个可以让你的Adobe Illustrator图变成canvas代码的东西,如果有非常棒,可以节省很多时间了。

绘画状态

在上一讲中,我们提到了如何更改填充和边框的样式。需要注意的一点是,如果你想要原来的笔画的粗细和颜色,那么需要再手动改回来。不过,还有更好的办法,称为绘画状态。

Canvas中的绘画状态是指你可以保存你当前的绘画样式,之后还能再改回来。这是一个稍微有点绕的感觉,但是在理解之后还是能做很多事情的。事实上,会装状态中保存了很多关于canvas的信息,比如转换矩阵,剪辑区域,还有下面这些属性:globalAlpha,globalCompositeOperation, strokeStyle, fillStyle, lineWidth,lineCap, lineJoin, miterLimit, shadowOffsetX, shadowOffsetY,shadowBlur, shadowColor, font, textAlign, 以及textBaseline。可能很多属性都还没用过,RockUX会在后面的文章中讲到。

保存绘画状态

使用绘画状态很简单,但是理解上还得花点时间。将上一节的代码替换如下:

1
2
3
ctx.fillStyle = "rgb(0,0,255)";
ctx.save();
ctx.fillRect(50,50,100,100);

要保存绘画状态,只需要调用这么一句就行了,很简单吧。

当你把当前的样式改为蓝色的时候,然后调用save方法,就会把当前的样式存入栈中。一般,绘画状态的栈是空的。

需要理解的是,栈就跟你桌子上的一沓文件一样,第一个入栈的就好像最底下的文件,后入栈的在最上面。如果你想获得最下面的文件,就得先拿掉上面的文件。可以理解为先进后出,或者后进先出。

恢复绘画状态

保存绘画状态很简单,但是保存了我们总还是要取出来的吧。你可以使用restore方法。

加上下面的代码:

1
2
ctx.fillStyle = "rgb(255,0,0)";
ctx.fillRect(200,50,100,100);

然后就会画出来另一个矩形,只不过颜色不同(这次是红色):

画完了红色的矩形,你又想画蓝色的了怎么办呢?可以手动设置成蓝色,不过这样太无聊了一点,我们试试这个方法吧:

1
2
ctx.restore()
ctx.fillRect(350, 50, 100, 100);

这会画出来另外一个蓝色的矩形:

很简单吧,调用restore方法之后,就会移除后来加入进去的样式,恢复到你存储时候的样式。这节省了很多时间。好吗,这个例子中没有节省你大量的时间,但是在做变换的时候你就体会到了。

使用多重的drawing state

你现在知道如何在简单的情况下使用恢复绘画状态,那么多重状态的时候怎么办呢?你应该还记得我之前提到的文件,后进,先出,我们看看下面的代码:

1
2
3
4
5
6
7
8
ctx.fillStyle = "rgb(0, 0, 255)";
ctx.save();
ctx.fillRect(50, 50, 100, 100);
ctx.fillStyle = "rgb(255, 0, 0)";
ctx.save();
ctx.fillRect(200, 50, 100, 100);
ctx.restore()
ctx.fillRect(350, 50, 100, 100);

虽然这和之前的代码差不多,但是在最后这点还是有很大不同的,我们最后看到的是这样:

如果想恢复最初的蓝色的状态,你需要在调用一次restore命令,所以需要添加:

1
2
ctx.restore();
ctx.fillRect(50, 200, 100, 100);

这会从栈里面移除最初添加进去的蓝色的样式,然后应用到canvas上面:

多次使用restore就能节省不少时间了。

总结一下

我们这次讲的不是太多,不过有些概念还是比较复杂的,最好还是自己动手试一试吧。

在RockUX后面的文章中,我们会讲到Canvas中的转换,以及阴影和渐变。很激动人心吧。[English]

转载请注明:
作者:RockUX-WEB前端
出自:【Canvas学习教程】Canvas高级特性

建议继续学习

  1. Canvas学习教程 : Canvas介绍 (阅读 4,201)
  2. 使用canvas绘制时钟 (阅读 3,600)
  3. HTML5 Canvas(画布)教程 (阅读 3,540)
  4. 使用JavaScript和Canvas开发游戏 (阅读 3,521)
  5. 云标签,关键字图排版 html5 canvas版(一) (阅读 3,361)
  6. 使用JavaScript和Canvas开发游戏(一) (阅读 3,021)
  7. 使用JavaScript和Canvas开发游戏(三) (阅读 2,640)
  8. 使用JavaScript和Canvas开发游戏(二) (阅读 2,561)
  9. 使用JavaScript和Canvas开发游戏(五) (阅读 2,301)
  10. 使用JavaScript和Canvas开发游戏(四) (阅读 1,981)