firefox图像创作____ 如何绘图篇(搜集整理)

来源:互联网 发布:ug4.0螺纹铣刀编程 编辑:程序博客网 时间:2024/05/23 01:58
这一篇我们来介绍如何通过canvas标签来绘制图形:

栅格(The grid)

     在我们开始画图前,我们先讨论下面板的网格或坐标。我画了一个我们之前在前文中我们提到的HTML模板里面的一个150*150大小的面板的布满栅格的效 果图,一般的说来,1栅格相当于面板上的1象素。栅格的原点在左上角(坐标(0,0))。所以的元素的定位都是相对于该原地的,这和其他语言中的绘图版的 坐标系统类似,所以,蓝色正方形的左上角的位置是应该是顶部x栏和左边y栏的距离(坐标(x,y))。

后面我们会看到如何将原点转换成其他位置、选择栅格甚至缩放它,现在我们只是使用默认的。

绘制基本图形( Drawing shapes)

与SVG不同, canvas只支持基本形状--矩形,其他的形状只能通过组合一个或多个路径(paths)来实现。幸运的是,我们有一系列的路径绘制函数来使得我们可以绘制出非常复杂的图形。((/images/firefox/Canvas_quadratic.png|

矩形

首先让我们看看矩形。canvas有3个绘制矩形的函数:

fillRect(x,y,width,height) : 绘制填充矩形
strokeRect(x,y,width,height) : 绘制矩形框
clearRect(x,y,width,height) : 清除特定的区域并且使该区域完成透明

三个函数都有相同的参数:x和y,表示矩形左上角在面板的位置(相对原点)。width和height的意思就不用我说了,现在我们动手来实战下。

以下是我们在第一篇介绍中提到的draw()函数, 但是我们添加了上面提到的三个函数的调用。

矩形示例

查看示例

function draw(){
var canvas = document.getElementById('tutorial');
if (canvas.getContext){
var ctx = canvas.getContext('2d');


ctx.fillRect(25,25,100,100);
ctx.clearRect(45,45,60,60);
ctx.strokeRect(50,50,50,50);
}
}

    右边是效果图,fillRect函数绘制了一个100x100的大正方形,clearRect函数在大正方形的中间移除了一个60x60象素的正方形,最后strokeRect 函数在清除区域内部绘制了一个50*50的矩形框。

后续的文章中,我们会介绍clearRect的两个替代方法以及怎样改变矩形填充颜色和绘制样式。

和我们马上要讲到的路径不同,所以的三个矩形函数都是一执行就立刻在面板上绘制出图形。

绘制路径

要使用路径绘制图形,我需要多个一系列特殊的步骤:

beginPath()
closePath()
stroke()
fill()

第1步调用beginPath方法创建路径,在系统内部,路径是作为一系列子路径(线、弧等)列表存储的,他们一起来构造成图形,每次这个方法调用的时候,该列表将被轻空重置,然后我们就可以看是绘制新的图形了。

第2步是做实际的绘制工作。

第3个可选的步骤,调用closePath方法,该方法试图在起始点和当前点之间绘制线条来封闭图形,如果该图形实际已经封闭或者列表中只有一个点,该函数什么效果都没有。

最后的步骤将要调用笔触(stroke)或/和填充方法,调用其中之一将实际在面板上绘制出图形。 笔触用来图形轮廓而填充方法用来填充块状区域图形。

注意:当在调用填充方法填充任何未封闭的图形时,将会自动封闭图形,所以这时候不必调用closePath方法。

以下是一个绘制三角形的代码:

ctx.beginPath(); ctx.moveTo(75,50); ctx.lineTo(100,75); ctx.lineTo(100,25); ctx.fill();

moveTo函数

虽然moveTo函数它并不实际绘制任何东西,但是这是一个非常的重要的方法,它是路径列表的一部分。你可以这样来理解该方法的作用,就如你的钢笔或者铅笔在纸上的一点移动到下一点。

moveTo(x, y)

moveTo函数有两个参数 - x 和 y, - 表示将要移动到的新的位置点的坐标。

     当面板初始化的时候或者beginPath方法被调用的时候,绘制的起始点被设置为原点(0,0),大部分情况下,我们需要使用moveTo方法将起始绘 制点移动到其他地方。我们也可以使用moveTo方法绘制不连续的路径,请看右边的笑脸,我已经标出了moveTo方法调用的位置(红线)。

moveTo示例

查看示例

ctx.beginPath();
ctx.arc(75,75,50,0,Math.PI*2,true); // Outer circle
ctx.moveTo(110,75);
ctx.arc(75,75,35,0,Math.PI,false); // Mouth (clockwise)
ctx.moveTo(65,65);
ctx.arc(60,65,5,0,Math.PI*2,true); // Left eye
ctx.moveTo(95,65);
ctx.arc(90,65,5,0,Math.PI*2,true); // Right eye
ctx.stroke();

注意:移除moveTo方法将看到一个连续的线条。
注意:后面我们会介绍arc函数的。

直线

使用lineTo方法要绘制直线:

lineTo(x, y)

该方法有两个参数 -- x和y,他们标明要绘制线条的截至点(终点)。起始点以来与前面的绘制路径有关,前次路径的截至点就是本次路径的起始点,起始点可以通过moveTo方法改变。

lineTo示例

    下面的示例,我们绘制了两个三角形,一个填充了一个只有边框(效果图见右边),首先调用beginPath方法开始绘制新的路径,然后我们使用moveTo方法江起始点移动到我们想要的位置,接着画了两条直线作为三角形的两边。

你应该注意到,填充三角形和三角形框的绘制代码有些不同,这是我们有意而为的,因为当路径被填充时,图形会自动封闭,但是对于一个只有两边的三角形框这样处理的画,这个三角形是不未完成的。

查看示例

// Filled triangle
ctx.beginPath();
ctx.moveTo(25,25);
ctx.lineTo(105,25);
ctx.lineTo(25,105);
ctx.fill();

// Stroked triangle
ctx.beginPath();
ctx.moveTo(125,125);
ctx.lineTo(125,45);
ctx.lineTo(45,125);
ctx.closePath();
ctx.stroke();

弧形

使用arc方法画弧形或者圆形。在规范中同时还有一个arcTo方法,但是在当前的Gecko浏览器中还未实现。

arc(x, y, radius, startAngle, endAngle, anticlockwise)

该方法有5个参赛:x和y坐标表示圆弧的中心坐标,Radius是圆的半径,startAngle和endAngle是 圆弧起始和截至点弧度,起始和截至点弧度是基于x坐标测量的。布尔型的anticlockwise(是否逆时针)参数,true表示逆时针方向绘制圆弧, false则为顺时针方向。

警告:在Beta版的Firefox中,最后一个参数是clockwise(是否顺时针),在最后发布版本中,与上面描述相同,所以Beta版的代码一直到正式版时需要修改您的代码。

注意: arc函数中的角度是弧度而不是度数,您可以使用该Javascript代码转换角度到弧度:var radians = (Math.PI/180)*degrees。

弧度示例

     下面示例比前面我们看到的任何示例都复杂得多,我们绘制了12个不同角度和填充的弧形,如果我们象前面绘制笑脸一样编码的话,我们首先代码将很长,其次, 我们需要知道所有圆弧的起始点和截至点弧度,我们用的是90、180和270度还好,但是如果更加复杂点的话,这将是个非常头痛的问题。

我们使用两个嵌套的循环来遍历圆弧,每个圆弧,我们使用beginPath来开始路径,接着我们将所有的参数存贮为变 量,以供后续的操作使用,一行一条,x和y坐标足够的清除,就不解释了,半径和起始弧度是固定的,截至弧度,初始是180度(PI,等式第1部分),每次 循环递增90°,clockwise参数设置语句的作用是,设置第1行和第3行顺时针绘制而2、4行逆时针绘制。最后的IF语句使得一般不填充一半填充。

for (i=0;i<4;i++){

for(j=0;j<3;j++){
ctx.beginPath();
var x = 25+j*50; // x coordinate
var y = 25+i*50; // y coordinate
var radius = 20; // Arc radius
var startAngle = 0; // Starting point on circle
var endAngle = Math.PI+(Math.PI*j)/2; // End point on circle
var clockwise = i%2==0 ? false : true; // clockwise or anticlockwise


ctx.arc(x,y,radius,startAngle,endAngle, clockwise);


if (i>1){
ctx.fill();
} else {
ctx.stroke();
}
}

}

贝塞尔与二次方程曲线( Bezier and quadratic curves)

下一种可用的路径是:贝塞尔与二次方程曲线,他们通常用来绘制复杂的自然曲线。

quadraticCurveTo(cp1x, cp1y, x, y)
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)

    右边的图片很好的说明了他们的区别,二次方程曲线有着一个起始点和一个截至点(蓝色点),并且只有一个控制点(红点),而贝塞儿曲线有两个控制点。

这两方法的x、y参数指的都是截至点的坐标,cp1x和cp1y是第1个控制点的坐标,))cp2x和cp2y是第2个控制点坐标。

使用贝塞尔与二次方程曲线有点难度,因为不像矢量画图软件如Adobe Illustrator那样,我们将要绘制的图形有直观的显示,这就使得要绘制一个复杂图形就非常困难。以下示例我们绘制一些比较简单的自然图形,但是如果您有时间,当然需要耐心,更加复杂的图形您也可以绘制出来。

这些示例都不是很难,每一条语句的成功绘制一条曲线,最终组合成一个完整的图形。

quadraticCurveTo示例

查看示例

// Quadratric curves example
ctx.beginPath();
ctx.moveTo(75,25);
ctx.quadraticCurveTo(25,25,25,62.5);
ctx.quadraticCurveTo(25,100,50,100);
ctx.quadraticCurveTo(50,120,30,125);
ctx.quadraticCurveTo(60,120,65,100);
ctx.quadraticCurveTo(125,100,125,62.5);
ctx.quadraticCurveTo(125,25,75,25);
ctx.stroke();

因为只有一个控制点,二次方程曲线很受限制,它的通常的用来绘制圆角,其他就没有什么更多作用了,更加平滑流畅的曲线需要使用贝塞尔曲线。使用有两个控制点的贝塞尔曲线绘制以上图形也是可以的。

bezierCurveTo示例

查看示例

// Bezier curves example
ctx.beginPath();
ctx.moveTo(75,40);
ctx.bezierCurveTo(75,37,70,25,50,25);
ctx.bezierCurveTo(20,25,20,62.5,20,62.5);
ctx.bezierCurveTo(20,80,40,102,75,120);
ctx.bezierCurveTo(110,102,130,80,130,62.5);
ctx.bezierCurveTo(130,62.5,130,25,100,25);
ctx.bezierCurveTo(85,25,75,37,75,40);
ctx.stroke();

矩形路径

前面提到了直接在面板绘制矩形的3个方法,我们还可以使用rect方法在路径上添加矩形。

rect(x, y, width, height)

该方法有4个参数,x、y是矩形路径的左上角坐标,width和height定义了矩形的宽和高。

执行完这个方法后,moveTo(0,0)方法将被自动执行(重置起始绘制点到默认位置(0,0))。

组合到一起

上面所以的例子中我们都只使用一种函数绘制一种图形,但是并没有任何限制我们在绘图时只能使用一种图形方法,定义您自己的绘制复杂图形的函数非常有用,一些示例中我们就将多次使用的代码定义成一个函数。

后续的介绍中我们会更加深入的介绍fillStyle函数,这里用它来依次改变填充颜色为白色或黑色。

查看示例

function draw() {

var ctx = document.getElementById('canvas').getContext('2d');
roundedRect(ctx,12,12,150,150,15);
roundedRect(ctx,19,19,150,150,9);
roundedRect(ctx,53,53,49,33,10);
roundedRect(ctx,53,119,49,16,6);
roundedRect(ctx,135,53,49,33,10);
roundedRect(ctx,135,119,25,49,10);


ctx.beginPath();
ctx.arc(37,37,13,Math.PI/7,-Math.PI/7,true);
ctx.lineTo(31,37);
ctx.fill();
for(i=0;i<8;i++){
ctx.fillRect(51+i*16,35,4,4);
}
for(i=0;i<6;i++){
ctx.fillRect(115,51+i*16,4,4);
}
for(i=0;i<8;i++){
ctx.fillRect(51+i*16,99,4,4);
}
ctx.beginPath();
ctx.moveTo(83,116);
ctx.lineTo(83,102);
ctx.bezierCurveTo(83,94,89,88,97,88);
ctx.bezierCurveTo(105,88,111,94,111,102);
ctx.lineTo(111,116);
ctx.lineTo(106.333,111.333);
ctx.lineTo(101.666,116);
ctx.lineTo(97,111.333);
ctx.lineTo(92.333,116);
ctx.lineTo(87.666,111.333);
ctx.lineTo(83,116);
ctx.fill();
ctx.fillStyle = "white";
ctx.beginPath();
ctx.moveTo(91,96);
ctx.bezierCurveTo(88,96,87,99,87,101);
ctx.bezierCurveTo(87,103,88,106,91,106);
ctx.bezierCurveTo(94,106,95,103,95,101);
ctx.bezierCurveTo(95,99,94,96,91,96);
ctx.moveTo(103,96);
ctx.bezierCurveTo(100,96,99,99,99,101);
ctx.bezierCurveTo(99,103,100,106,103,106);
ctx.bezierCurveTo(106,106,107,103,107,101);
ctx.bezierCurveTo(107,99,106,96,103,96);
ctx.fill();
ctx.fillStyle = "black";
ctx.beginPath();
ctx.arc(101,102,2,0,Math.PI*2,true);
ctx.fill();
ctx.beginPath();
ctx.arc(89,102,2,0,Math.PI*2,true);
ctx.fill();

}
function roundedRect(ctx,x,y,width,height,radius){

ctx.beginPath();
ctx.moveTo(x,y+radius);
ctx.lineTo(x,y+height-radius);
ctx.quadraticCurveTo(x,y+height,x+radius,y+height);
ctx.lineTo(x+width-radius,y+height);
ctx.quadraticCurveTo(x+width,y+height,x+width,y+height-radius);
ctx.lineTo(x+width,y+radius);
ctx.quadraticCurveTo(x+width,y,x+width-radius,y);
ctx.lineTo(x+radius,y);
ctx.quadraticCurveTo(x,y,x,y+radius);
ctx.stroke();
}