Do what you do

canvas元素简易教程(3)(大部分转自火狐,自己只写了简单的代码分析)

记得我们上次学了什么么?矩形、路径、填充。回忆一下以前的东西,我们有什么用到却没有学过的呢?对了,那一个大笑脸不是都是圆弧么,它咋做到的呢?

别急别急,咱们慢慢来~

我们用arc方法来绘制弧线或圆。标准说明中还包含arcTo方法,当前Safari是支持的,但基于Gecko的浏览器还未实现。知道这是啥意思么?就是告诉你arcTo方法现在先别用。。。

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

方法接受五个参数:x,y是圆心坐标radius是半径,startAngle和endAngle分别是起末弧度(以x轴为基准),anticlockwise为true表示逆时针,反之顺时针。

这个似乎略微复杂,没事,在上例子前我们一起分析一下。圆心坐标不用说了吧,起末弧度是啥意思?这样说吧,一个圆是360°,也就是2π弧度,以圆心为坐标原点,开始计算起始弧度与终止弧度,即在圆心右侧为0,左侧为π,上方为3/2π。顺时针还是逆时针就是画线的方向了,比如0到π/2,顺时针就是四分之一个圆,逆时针就是四分之三个圆。

废话太多了,来上代码吧:

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

   for(j=0;j<3;j++){    //chinese_xu 原始代码

    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 anticlockwise  = i%2==0 ? false : true; // clockwise or anticlockwise

 

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

 

    if (i>1){

      ctx.fill();

    } else {

      ctx.stroke();

    }

  }

}

这个示例比之前见到过的要复杂一些,画了12个不同的弧形,有不同夹角和填充状态的。如果我用上面画笑脸的方式来画这些弧形,那会是一大段的代码,而且,画每一个弧形时我都需要知道其圆心位置。像我这里画90,180和270度的弧形看起来不是很麻烦,但是如果图形更复杂一些,则实现起来会越来越困难。

这里使用两个for循环来画多行多列的弧形。每一个弧形都用beginPath方法创建一个新路径。然后为了方便阅读和理解,我把所有参数都写成变量形式。显而易见x和y作为圆心坐标。radius和startAngle都是固定,endAngle从180度半圆开始,以90度方式递增至圆anticlockwise则取决于奇偶行数。最后,通过if语句判断使前两行表现为勾边,而后两行为填充效果。

我的建议是,拿到代码之后,咱都试试。试试去分析代码才能更好的理解。所以,让我们来消化一下这段代码吧。

好了,消化好了么?其实画圆也并不难对么?但是不要忘记了,很少有网站会给予圆心坐标和弧度让你去画圆玩的,比如用DOM的延时和循环来控制,显示一个圆的绘画过程;再或者展现无数的圆一个一个画出来的过程。怎么样,是不是难了点呢?其实都是用基本的方法,做出来的却是不一样的复杂效果,这些就需要大量的练习与学习了。多动手,一切都会变得简单起来。

接下来要介绍的路径是贝塞尔曲线 ,它可以是二次和三次方的形式,一般用于绘制复杂而有规律的形状。

对于笔者来说,这个,实在是太恶心了。。。你们知道贝塞尔曲线么?要是不知道我告诉你,贝塞尔曲线就是依据四个位置任意的点坐标绘制出的一条光滑曲线(就这样理解吧)。你说怎么去计算这个平滑曲线?我就知道你会问这个,你太可恶了。。。我要说我也不知道肯定会被你嘲笑的,来吧,看这里:

线性贝塞尔曲线公式:B(t)=P0+(P1-P0)t,t∈[0,1]

二次方贝塞尔曲线公式:B(t)=(1-t)2P0+2t(1-t)P1+t2P2,t∈[0,1]

三次方贝塞尔曲线公式:咱不说了吧,又不是搞数学的,这个只能画二维的,放弃吧。。。

让我们来看一下方法的定义吧:

quadraticCurveTo(cp1x, cp1y, x, y) // BROKEN in Firefox 1.5 (see work around below)

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

上面两行代码都有一个起点一个终点,但二次方贝塞尔曲线只有一个控制点,而三次方贝塞尔曲线有两个。你要问一个与两个有什么区别,好吧,一个控制点就是在两点之间只有一个点帮助你做平滑曲线,两个点就是始末点中间有两个点帮助你做平滑。当然,两点要是在你始末点连线的两侧的话,那就成一个S形咯。。。

参数x和y是终点坐标,cp1x和cp1y是第一个控制点的坐标,cp2x和cp2y是第二个的。

友情提示,拿这玩画曲线很有挑战,你企图计算出来应该怎么画得话,我建议还是试吧,多试就熟了。只要你愿意花时间,多恶心的东西理论上都能画出来。

废话太多了,直接上例子:

// 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();

通过计算,可以由二次曲线的单个控制点得出相应三次方曲线的两个控制点,因此二次方转三次方是可能的,但是反之不然。仅当三次方程中的三次项为零是才可能转换为二次的贝塞尔曲线。通常地可以用多条二次方曲线通过细分算法来近似模拟三次方贝塞尔曲线。

我觉得吧,这个东西还在于多使,或者就别使了。。。开玩笑的,虽然有点难度,但有的时候需要平滑曲线的时候只能拿这个东西画,还是多加练习的好。

上面的例子是用二次方绘图,一个类似漫画中的说话的图标就出来了。现在咱来看个三次方的:

// 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.fill();

这段代码画了一个心形,利用三次方的特性绘制平滑曲线。我觉得我们可以去尝试画一个椭圆或者一个伞形,然后你会对这个贝塞尔曲线有一个新的认识。不信,你试试?

在Firefox 1.5里,quadatricCurveTo()的实现是有bug的,它不是直接绘制二次方曲线,而是调用bezierCurveTo(),其中两个控制点都是二次方曲线的那个单控制点。因此,它会绘制出不正确的曲线。如果必须使用到quadraticCurveTo(),你需要自行去将二次方曲线转换成三次方的,这样就可以用bezierCurveTo()方法了。好了,上代码:

var currentX, currentY;  // set to last x,y sent to lineTo/moveTo/bezierCurveTo or quadraticCurveToFixed()

 

function quadraticCurveToFixed( cpx, cpy, x, y ) {

  /*

   For the equations below the following variable name prefixes are used:

     qp0 is the quadratic curve starting point (you must keep this from your last point sent to moveTo(), lineTo(), or bezierCurveTo() ).

     qp1 is the quadatric curve control point (this is the cpx,cpy you would have sent to quadraticCurveTo() ).

     qp2 is the quadratic curve ending point (this is the x,y arguments you would have sent to quadraticCurveTo() ).

   We will convert these points to compute the two needed cubic control points (the starting/ending points are the same for both

   the quadratic and cubic curves.

 

   The equations for the two cubic control points are:

     cp0=qp0 and cp3=qp2

     cp1 = qp0 + 2/3 *(qp1-qp0)

     cp2 = cp1 + 1/3 *(qp2-qp0)

 

   In the code below, we must compute both the x and y terms for each point separately.

 

    cp1x = qp0x + 2.0/3.0*(qp1x - qp0x);

    cp1y = qp0y + 2.0/3.0*(qp1y - qp0y);

    cp2x = cp1x + (qp2x - qp0x)/3.0;

    cp2y = cp1y + (qp2y - qp0y)/3.0;

 

   We will now

     a) replace the qp0x and qp0y variables with currentX and currentY (which *you* must store for each moveTo/lineTo/bezierCurveTo)

     b) replace the qp1x and qp1y variables with cpx and cpy (which we would have passed to quadraticCurveTo)

     c) replace the qp2x and qp2y variables with x and y.

   which leaves us with:

  */

  var cp1x = currentX + 2.0/3.0*(cpx - currentX);

  var cp1y = currentY + 2.0/3.0*(cpy - currentY);

  var cp2x = cp1x + (x - currentX)/3.0;

  var cp2y = cp1y + (y - currentY)/3.0;

 

  // and now call cubic Bezier curve to function

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

 

  currentX = x;

  currentY = y;

}

这段代码看懂了原理给我讲一下,我懒得去看了。。。

好了,今天的东西就到这里了,剩下的,咱们明天见~

posted @ 2012-03-13 18:22  key yao  阅读(287)  评论(0编辑  收藏  举报
学会做事 学会做人